diff --git a/frontend/bindings/app/internal/services/posttypeservice.ts b/frontend/bindings/app/internal/services/posttypeservice.ts index e432747..f4445ff 100644 --- a/frontend/bindings/app/internal/services/posttypeservice.ts +++ b/frontend/bindings/app/internal/services/posttypeservice.ts @@ -50,6 +50,11 @@ export function GetById(id: number): Promise<$models.PostType | null> & { cancel return $typingPromise; } +export function ImportFromExcel(): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(1671814244) as any; + return $resultPromise; +} + export function Update(item: $models.PostType): Promise<$models.PostType> & { cancel(): void } { let $resultPromise = $Call.ByID(2773888269, item) as any; let $typingPromise = $resultPromise.then(($result: any) => { diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 504505b..adb0b25 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,18 +1,27 @@ diff --git a/frontend/src/post/PostScheme.vue b/frontend/src/post/PostScheme.vue index 2849989..0e62307 100644 --- a/frontend/src/post/PostScheme.vue +++ b/frontend/src/post/PostScheme.vue @@ -22,112 +22,111 @@ const service = new Service(); const items = ref([]); const load = async () => { - (scheme as any).Author.type!.nested!.values = await authorService.readAll(); + (scheme as any).Author.type!.nested!.values = await authorService.readAll(); - (scheme as any).PostType.type!.nested!.values = - await posttypeService.readAll(); + (scheme as any).PostType.type!.nested!.values = + await posttypeService.readAll(); - (scheme as any).Comments.type!.nested!.values = - await commentService.readAll(); + (scheme as any).Comments.type!.nested!.values = + await commentService.readAll(); - items.value = await service.readAll(); - return items.value; + items.value = await service.readAll(); + return items.value; }; onMounted(async () => { - load(); + await load(); }); const scheme: Scheme = reactive({ - entityId: "PostId", + entityId: "PostId", - Id: { - hidden: true, - type: { - primitive: "number", + Id: { + hidden: true, + type: { + primitive: "number", + }, }, - }, - Text: { - russian: "Текст", - type: { - primitive: "string", + Text: { + russian: "Текст", + type: { + primitive: "string", + }, }, - }, - Deadline: { - russian: "Дедлайн", - date: true, - type: { - primitive: "date", + Deadline: { + russian: "Дедлайн", + date: true, + type: { + primitive: "date", + }, }, - }, - CreatedAt: { - readonly: true, - date: true, - type: { - primitive: "date", + CreatedAt: { + russian: "Дата создания", + type: { + primitive: "date", + }, }, - }, - AuthorId: { - hidden: true, - type: { - primitive: "number", + AuthorId: { + hidden: true, + type: { + primitive: "number", + }, }, - }, - Author: { - russian: "Автор", - type: { - nested: { - values: [], - field: ["Name"], - }, + Author: { + russian: "Автор", + type: { + nested: { + values: [], + field: ["Name"], + }, + }, }, - }, - PostTypeId: { - hidden: true, - type: { - primitive: "number", + PostTypeId: { + hidden: true, + type: { + primitive: "number", + }, }, - }, - PostType: { - russian: "Тип", - type: { - nested: { - values: [], - field: ["Name"], - }, + PostType: { + russian: "Тип", + type: { + nested: { + values: [], + field: ["Name"], + }, + }, }, - }, - Comments: { - russian: "Комментарии", - many: true, - type: { - nested: { - values: [], - field: ["Text"], - }, + Comments: { + russian: "Комментарии", + many: true, + type: { + nested: { + values: [], + field: ["Text"], + }, + }, }, - }, }); const getDefaults = () => getDefaultValues(scheme); const validate: Validate = (entity) => { - return { - status: "success", - }; + return { + status: "success", + }; }; diff --git a/frontend/src/posttype/PosttypeScheme.vue b/frontend/src/posttype/PosttypeScheme.vue index 366a83b..5d93377 100644 --- a/frontend/src/posttype/PosttypeScheme.vue +++ b/frontend/src/posttype/PosttypeScheme.vue @@ -7,49 +7,51 @@ import type { Scheme } from "../types/scheme.type"; import { PostType } from "../../bindings/app/internal/services"; import { ref } from "vue"; import type { Validate } from "../types/validate.type"; +import { ImportFromExcel } from "../../bindings/app/internal/services/posttypeservice.ts"; const service = new Service(); const items = ref([]); const load = async () => { - items.value = await service.readAll(); - return items.value; + items.value = await service.readAll(); + return items.value; }; onMounted(async () => { - load(); + await load(); + await ImportFromExcel(); }); const scheme: Scheme = reactive({ - entityId: "PostTypeId", + entityId: "PostTypeId", - Id: { - hidden: true, - type: { - primitive: "number", + Id: { + hidden: true, + type: { + primitive: "number", + }, }, - }, - Name: { - russian: "Название", - type: { - primitive: "string", + Name: { + russian: "Название", + type: { + primitive: "string", + }, }, - }, }); const getDefaults = () => getDefaultValues(scheme); const validate: Validate = (entity) => { - return { - status: "success", - }; + return { + status: "success", + }; }; diff --git a/go.mod b/go.mod index 7908323..84d4e75 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.22.4 toolchain go1.23.4 require ( - github.com/wailsapp/wails/v3 v3.0.0-alpha.8.3 + github.com/wailsapp/wails/v3 v3.0.0-alpha.9 github.com/xuri/excelize/v2 v2.9.0 - gorm.io/driver/sqlite v1.5.0 + gorm.io/driver/sqlite v1.5.7 gorm.io/gen v0.3.26 gorm.io/gorm v1.25.12 gorm.io/plugin/dbresolver v1.5.3 @@ -42,7 +42,7 @@ require ( github.com/lmittmann/tint v1.0.4 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.16 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect @@ -52,7 +52,7 @@ require ( github.com/samber/lo v1.38.1 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/skeema/knownhosts v1.2.2 // indirect - github.com/wailsapp/go-webview2 v1.0.18 // indirect + github.com/wailsapp/go-webview2 v1.0.19 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect @@ -65,6 +65,7 @@ require ( golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/tools v0.28.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gorm.io/datatypes v1.2.5 // indirect gorm.io/driver/mysql v1.5.7 // indirect diff --git a/go.sum b/go.sum index e62e9df..693b030 100644 --- a/go.sum +++ b/go.sum @@ -96,8 +96,9 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= @@ -134,12 +135,12 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/wailsapp/go-webview2 v1.0.18 h1:SSSCoLA+MYikSp1U0WmvELF/4c3x5kH8Vi31TKyZ4yk= -github.com/wailsapp/go-webview2 v1.0.18/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= +github.com/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyTImA6NU= +github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= -github.com/wailsapp/wails/v3 v3.0.0-alpha.8.3 h1:9aCL0IXD60A5iscQ/ps6f3ti3IlaoG6LQe0RZ9JkueU= -github.com/wailsapp/wails/v3 v3.0.0-alpha.8.3/go.mod h1:9Ca1goy5oqxmy8Oetb8Tchkezcx4tK03DK+SqYByu5Y= +github.com/wailsapp/wails/v3 v3.0.0-alpha.9 h1:b8CfRrhPno8Fra0xFp4Ifyj+ogmXBc35rsQWvcrHtsI= +github.com/wailsapp/wails/v3 v3.0.0-alpha.9/go.mod h1:dSv6s722nSWaUyUiapAM1DHc5HKggNGY1a79shO85/g= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7psK/lVsjIS2otl+1WyRyY= @@ -226,6 +227,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -238,8 +241,9 @@ gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U= gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A= -gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c= gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I= +gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I= +gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= gorm.io/driver/sqlserver v1.5.4 h1:xA+Y1KDNspv79q43bPyjDMUgHoYHLhXYmdFcYPobg8g= gorm.io/driver/sqlserver v1.5.4/go.mod h1:+frZ/qYmuna11zHPlh5oc2O6ZA/lS88Keb0XSH1Zh/g= gorm.io/gen v0.3.26 h1:sFf1j7vNStimPRRAtH4zz5NiHM+1dr6eA9aaRdplyhY= diff --git a/internal/dialogs/dialogs.go b/internal/dialogs/dialogs.go index ebeb937..aafbc30 100644 --- a/internal/dialogs/dialogs.go +++ b/internal/dialogs/dialogs.go @@ -1,44 +1,45 @@ package dialogs import ( + "fmt" + "github.com/wailsapp/wails/v3/pkg/application" ) -var currentWindow *application.WebviewWindow - -func Init(window *application.WebviewWindow) { - if window == nil { - panic("currentWindow is nil") - } - currentWindow = window -} - -func checkInit() { - if currentWindow == nil { - panic("Initialize dialogs package before use") - } -} - func InfoDialog(title string, message string) { - checkInit() - application.InfoDialog().AttachToWindow(currentWindow).SetTitle(title).SetMessage(message).Show() + application.InfoDialog().SetTitle(title).SetMessage(message).Show() } func WarningDialog(title string, message string) { - checkInit() - application.WarningDialog().AttachToWindow(currentWindow).SetTitle(title).SetMessage(message).Show() + application.WarningDialog().SetTitle(title).SetMessage(message).Show() } func ErrorDialog(title string, message string) { - checkInit() - application.ErrorDialog().AttachToWindow(currentWindow).SetTitle(title).SetMessage(message).Show() + application.ErrorDialog().SetTitle(title).SetMessage(message).Show() } func SaveFileDialog(title string, filename string) (string, error) { - checkInit() - selection, err := application.SaveFileDialog().AttachToWindow(currentWindow).SetFilename(filename).PromptForSingleSelection() + selection, err := application.SaveFileDialog().SetFilename(filename).PromptForSingleSelection() if err != nil { return "", err } return selection, nil } + +func OpenFileDialog(title string) (string, error) { + defer func() { + if r := recover(); r != nil { + fmt.Println("Recovered in f", r) + } + }() + dialog := application.OpenFileDialog() + dialog.SetTitle(title) + dialog.CanChooseDirectories(false) + dialog.AddFilter("Электронные таблицы (.xlsx)", "*.xlsx") + dialog.AddFilter("Электронные таблицы (.xls)", "*.xls") + path, err := dialog.PromptForSingleSelection() + if err != nil { + return "", err + } + return path, nil +} diff --git a/internal/extras/excel/import.go b/internal/extras/excel/import.go new file mode 100644 index 0000000..45a391a --- /dev/null +++ b/internal/extras/excel/import.go @@ -0,0 +1,53 @@ +package excel + +import ( + "fmt" + "log/slog" + + "github.com/xuri/excelize/v2" +) + +type Importer struct { + SheetName string + Loader func(rowIndex int, row []string) error + StartFromRowIdx int +} + +func ImportEntitiesFromSpreadsheet(path string, importers ...Importer) error { + for _, importer := range importers { + err := ImportEntitiesFromSpreadsheet(path, importer) + if err != nil { + return err + } + } + return nil +} + +func ImportFromSpreadsheet(filepath string, importer Importer) error { + f, err := excelize.OpenFile(filepath) + defer func() { + err := f.Close() + if err != nil { + slog.Error(fmt.Sprintf("Failed to close file: %s", err)) + } + }() + + if err != nil { + return err + } + + rows, err := f.GetRows(importer.SheetName) + + if err != nil { + return err + } + + for i, row := range rows { + err := importer.Loader(i, row) + if err != nil { + return err + } + } + + return nil +} diff --git a/internal/models/models.go b/internal/models/models.go index ff62b8d..1d804cf 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -13,7 +13,7 @@ type Post struct { Id uint `gorm:"primaryKey" ui:"hidden"` Text string `ui:"label:Текст"` Deadline int64 `ui:"label:Дедлайн;datatype:datetime;"` - CreatedAt int64 `gorm:"autoCreateTime" ui:"readonly;datatype:datetime;"` + CreatedAt int64 `gorm:"autoCreateTime" ui:"label:Время создания; readonly; datatype:datetime;"` AuthorId uint `ui:"hidden" gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"` Author Author `ui:"label:Автор; field:Name;"` PostTypeId uint `ui:"hidden"` diff --git a/internal/services/migrator.go b/internal/services/migrator.go index b1589e2..70cab96 100644 --- a/internal/services/migrator.go +++ b/internal/services/migrator.go @@ -6,10 +6,11 @@ import ( "context" "crypto/sha256" "fmt" - "github.com/wailsapp/wails/v3/pkg/application" "io" "log/slog" "os" + + "github.com/wailsapp/wails/v3/pkg/application" ) type Migrator struct{} @@ -77,9 +78,9 @@ func Migrate(entities ...any) error { err = db.AutoMigrate(entities...) if err != nil { - slog.Info("Error occurred while migrations: %s. Recreate database...", err) + slog.Info(fmt.Sprintf("Error occurred while migrations: %s, Recreate database...", err)) if err = createDatabase(entities...); err != nil { - slog.Error("Error occurred again: %s. Panic!", err) + slog.Error(fmt.Sprintf("Error occurred again: %s. Panic!", err)) return err } } else { @@ -87,7 +88,7 @@ func Migrate(entities ...any) error { var hashAfterMigration string hashAfterMigration, err = CalculateFileSha256Checksum(database.Path) if err != nil { - slog.Error("Failed to calc hash: %s", err) + slog.Error(fmt.Sprintf("Failed to calc hash: %s", err)) return err } @@ -95,7 +96,7 @@ func Migrate(entities ...any) error { slog.Info("Hashes before and after migrations are different. Recreate database...") err = createDatabase(entities...) if err != nil { - slog.Error("Failed to create new database: %s", err) + slog.Error(fmt.Sprintf("Failed to create new database: %s", err)) return err } } diff --git a/internal/services/posttype.go b/internal/services/posttype.go index 2a1a07a..5b196c6 100644 --- a/internal/services/posttype.go +++ b/internal/services/posttype.go @@ -3,8 +3,13 @@ package services import ( "app/internal/dal" "app/internal/database" + "app/internal/dialogs" + "app/internal/extras/excel" "app/internal/models" "errors" + "os/signal" + "strconv" + "syscall" "gorm.io/gen/field" "gorm.io/gorm" @@ -59,3 +64,30 @@ func (service *PostTypeService) Count() (int64, error) { amount, err := dal.PostType.Count() return amount, err } + +func (service *PostTypeService) ImportFromExcel() error { + signal.Ignore(syscall.SIGSEGV) + filepath, err := dialogs.OpenFileDialog("Импорт данных") + if err != nil { + return err + } + err = excel.ImportFromSpreadsheet(filepath, excel.Importer{ + SheetName: "Тип поста", + Loader: func(rowIndex int, row []string) error { + id, err := strconv.Atoi(row[0]) + if err != nil { + return err + } + + service.Create(PostType{ + Id: uint(id), + Name: row[1], + }) + return nil + }, + }) + if err != nil { + return err + } + return nil +} diff --git a/main.go b/main.go index 5f5e3e9..cba527c 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,6 @@ package main import ( - "app/internal/dialogs" "app/internal/services" "embed" "github.com/wailsapp/wails/v3/pkg/application" @@ -14,7 +13,7 @@ var assets embed.FS func main() { app := application.New(application.Options{ Name: "nto_starterkit", - Description: "A demo of using raw HTML & CSS", + Description: "NTO toolkit template", Services: append([]application.Service{services.MigratorService}, services.ExportedServices...), Assets: application.AssetOptions{ Handler: application.AssetFileServerFS(assets), @@ -29,16 +28,15 @@ func main() { // 'Mac' options tailor the window when running on macOS. // 'BackgroundColour' is the background colour of the window. // 'URL' is the URL that will be loaded into the webview. - window := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Завод \"Белочка\"", Mac: application.MacWindow{ InvisibleTitleBarHeight: 50, Backdrop: application.MacBackdropTranslucent, TitleBar: application.MacTitleBarHiddenInset, }, - URL: "/", + URL: "/", }) - dialogs.Init(window) err := app.Run()