diff --git a/SimplyCPQ2.code-workspace b/SimplyCPQ2.code-workspace new file mode 100644 index 0000000..876a149 --- /dev/null +++ b/SimplyCPQ2.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/buildtime/cmd/config.toml b/buildtime/cmd/config.toml new file mode 100644 index 0000000..870a095 --- /dev/null +++ b/buildtime/cmd/config.toml @@ -0,0 +1,17 @@ +# Database configuration +# Connection string is in Go pq driver format: +# host= port= user= +# password= dbname= + +[database] +connection_string = "host=192.168.178.50 port=5432 user=tsindt password=XD14ko8d dbname=xcpq sslmode=disable" +##connection_string = "host=localhost port=5432 user=tsindt password=XD14ko8d dbname=xcpq sslmode=disable" +driver_name = "postgres" + +################################################################ +# HTTP server configuration + +[http] +server_address = ":8088" + +################################################################ diff --git a/buildtime/cmd/main.go b/buildtime/cmd/main.go new file mode 100644 index 0000000..8aef96a --- /dev/null +++ b/buildtime/cmd/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "log" + + _ "github.com/lib/pq" + + "SimplyCPQ2/cmd/server" + "SimplyCPQ2/internal/config" +) + +func main() { + log.Println("Starting XCPQ Editor Service") + log.Println("Reading configuration") + config := config.InitConfig("config") + log.Println("Initializing database") + dbHandler := server.InitDatabase(config) + log.Println("Initializing HTTP server") + httpServer := server.InitHttpServer(config, dbHandler) + httpServer.Start() + + log.Println("XCPQ Editor Service Stopped") +} diff --git a/buildtime/cmd/server/dbServer.go b/buildtime/cmd/server/dbServer.go new file mode 100644 index 0000000..c060836 --- /dev/null +++ b/buildtime/cmd/server/dbServer.go @@ -0,0 +1,26 @@ +package server + +import ( + "log" + "database/sql" + "github.com/spf13/viper" + _ "github.com/lib/pq" +) + +func InitDatabase(config *viper.Viper) *sql.DB { + connectionString := config.GetString("database.connection_string") + driverName := config.GetString("database.driver_name") + if connectionString == "" { + log.Fatalf("Database connection string is missing") + } + dbHandler, err := sql.Open(driverName, connectionString) + if err != nil { + log.Fatalf("Error while initializing database: %v", err) + } + err = dbHandler.Ping() + if err != nil { + dbHandler.Close() + log.Fatalf("Error while validating database: %v", err) + } + return dbHandler +} \ No newline at end of file diff --git a/buildtime/cmd/server/httpServer.go b/buildtime/cmd/server/httpServer.go new file mode 100644 index 0000000..1854b3d --- /dev/null +++ b/buildtime/cmd/server/httpServer.go @@ -0,0 +1,104 @@ +package server + +import ( + handler "SimplyCPQ2/internal/handler" + "SimplyCPQ2/internal/repositories" + "SimplyCPQ2/internal/services" + "database/sql" + "log" + "net/http" + + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" + "github.com/spf13/viper" +) + +type HttpServer struct { + config *viper.Viper + router *gin.Engine + dbHandler *sql.DB + importHandler *handler.ImportHandler + importRepository *repositories.ImportRepository + valueRepository *repositories.ValueRepository + propertyRepository *repositories.PropertyRepository + propertyTypeRepository *repositories.PropertyTypeRepository + propertyTypeGroupRepository *repositories.PropertyTypeGroupRepository +} + +func InitHttpServer(config *viper.Viper, dbHandler *sql.DB) HttpServer { + + // db initialization can be done here if needed + + // repository initialisation: + valueRepository := repositories.NewValueRepository(dbHandler) + propertyRepository := repositories.NewPropertyRepository(dbHandler, valueRepository) + propertyTypeRepository := repositories.NewPropertyTypeRepository(dbHandler, propertyRepository) + propertyTypeGroupRepository := repositories.NewPropertyTypesGroupRepository(dbHandler, propertyTypeRepository) + importRepository := repositories.NewImportRepository(dbHandler, valueRepository, propertyTypeRepository, propertyTypeGroupRepository, propertyRepository) + + // service initialisation: + importService := services.NewImportService(importRepository) + propertyTypesService := services.NewPropertyTypeService(propertyTypeRepository, propertyTypeGroupRepository) + + // handler initialisation: + importHandler := handler.NewImportHandler(importService) + mainHandler := handler.NewMainHandler(propertyTypesService) + propertyTypeHandler := handler.NewPropertyTypeHandler(propertyTypesService) + + // Service API + router := gin.Default() + + // initialize templates + //router.LoadHTMLGlob("../templates/**/*.html") + + router.Use(cors.Default()) + + // static content + router.Static("/static", "../static") + router.GET("/", mainHandler.Home) + router.GET("/properties/rootnodes", propertyTypeHandler.GetRootPropertyTypeGroups) + + //router.GET("/properties", handler.PropertiesHandler) + /* old routes + // properties + router.GET("/properties/:id/childs", PropertyTypeHandler.GetPropertyTypesGroupDirectChildsById) + */ + + /* + router.GET("/products", handlers.ProductsHandler) + router.GET("/conditions", handlers.ConditionsHandler) + router.GET("/translations", handlers.TranslationsHandler) + */ + + // import + router.POST("/importPropertyTypes", importHandler.ImportPropertyTypeGroups) + + // test endpoint + router.GET("/test", Test) + + // server + return HttpServer{ + config: config, + router: router, + dbHandler: dbHandler, + importHandler: importHandler, + importRepository: importRepository, + valueRepository: valueRepository, + propertyRepository: propertyRepository, + propertyTypeRepository: propertyTypeRepository, + propertyTypeGroupRepository: propertyTypeGroupRepository, + } +} + +func (hs HttpServer) Start() { + err := hs.router.Run(hs.config.GetString("http.server_address")) + if err != nil { + log.Fatalf("Error while starting HTTP server: %v", err) + } +} + +func Test(ctx *gin.Context) { + ctx.JSON(http.StatusOK, gin.H{ + "message": "Test", + }) +} diff --git a/buildtime/cmd/tmp/build-errors.log b/buildtime/cmd/tmp/build-errors.log new file mode 100644 index 0000000..05e5985 --- /dev/null +++ b/buildtime/cmd/tmp/build-errors.log @@ -0,0 +1 @@ +exit status 1 \ No newline at end of file diff --git a/buildtime/cmd/tmp/main b/buildtime/cmd/tmp/main new file mode 100644 index 0000000..2691b60 Binary files /dev/null and b/buildtime/cmd/tmp/main differ diff --git a/buildtime/go.mod b/buildtime/go.mod new file mode 100644 index 0000000..2561fb9 --- /dev/null +++ b/buildtime/go.mod @@ -0,0 +1,51 @@ +module SimplyCPQ2 + +go 1.25.0 + +require ( + github.com/gin-contrib/cors v1.7.8 + github.com/gin-gonic/gin v1.12.0 + github.com/lib/pq v1.12.3 + github.com/spf13/viper v1.21.0 +) + +require ( + github.com/bytedance/gopkg v0.1.4 // indirect + github.com/bytedance/sonic v1.15.2 // indirect + github.com/bytedance/sonic/loader v0.5.1 // indirect + github.com/cloudwego/base64x v0.1.7 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect + github.com/gin-contrib/sse v1.1.1 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.30.3 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/goccy/go-json v0.10.6 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.4.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.23 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.4.3 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.60.0 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.1 // indirect + go.mongodb.org/mongo-driver/v2 v2.8.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/arch v0.29.0 // indirect + golang.org/x/crypto v0.55.0 // indirect + golang.org/x/net v0.57.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.41.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect +) diff --git a/buildtime/go.sum b/buildtime/go.sum new file mode 100644 index 0000000..ed88d9e --- /dev/null +++ b/buildtime/go.sum @@ -0,0 +1,124 @@ +github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM= +github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4= +github.com/bytedance/sonic v1.15.2 h1:90H+rcF/FwLXwfB1cudOLq/je83n683Utf4Cbp0xHCo= +github.com/bytedance/sonic v1.15.2/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA= +github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI= +github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI= +github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gin-contrib/cors v1.7.8 h1:U0jjyXlWXMEx27hFE6hmdg/TOZviYgsOGGT+kLmUZrc= +github.com/gin-contrib/cors v1.7.8/go.mod h1:u3nLI2pP1IlJn7tbvL7iiubDO9quBZ8FXP9LxmqpsPI= +github.com/gin-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko= +github.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s= +github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= +github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8= +github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= +github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw= +github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= +github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/mattn/go-isatty v0.0.23 h1:cYwCQTQf3HB6xUC+BtyCLZNr7IzbOmoZbmssVNzSyiQ= +github.com/mattn/go-isatty v0.0.23/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= +github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0= +github.com/quic-go/go-ossfuzz-seeds v0.1.0/go.mod h1:3IOHRbJIc+L6YKMwfDtJAM9Vj9k0YY4muhuyUYk5tbk= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.60.0 h1:xcQioE8OM66UQLeUMHltK1CCcOu3JbVB4JAQdDQSB+0= +github.com/quic-go/quic-go v0.60.0/go.mod h1:wpKpjmPpftl30sL6pFh7REVpjbcCVy4zt2vDyK1TuJk= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= +github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +go.mongodb.org/mongo-driver/v2 v2.8.0 h1:CxWDGQYY8QQwNjAl/aq2sfWakdnWZynnqJ9F4DhHbP8= +go.mongodb.org/mongo-driver/v2 v2.8.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/arch v0.29.0 h1:8sSET5wB0+exBm0FGmOtdHMqjlRdV2DRD3/IV6OZgho= +golang.org/x/arch v0.29.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8= +golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M= +golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/buildtime/internal/config/config.go b/buildtime/internal/config/config.go new file mode 100644 index 0000000..5d1da62 --- /dev/null +++ b/buildtime/internal/config/config.go @@ -0,0 +1,19 @@ +package config + +import ( + "log" + + "github.com/spf13/viper" +) + +func InitConfig(fileName string) *viper.Viper { + config := viper.New() + config.SetConfigName(fileName) + config.AddConfigPath(".") + config.AddConfigPath("$HOME") + err := config.ReadInConfig() + if err != nil { + log.Fatal("Error while parsing configuration file! ", err) + } + return config +} diff --git a/buildtime/internal/handler/importHandler.go b/buildtime/internal/handler/importHandler.go new file mode 100644 index 0000000..c34afa6 --- /dev/null +++ b/buildtime/internal/handler/importHandler.go @@ -0,0 +1,21 @@ +package handlers + +import ( + "SimplyCPQ2/internal/services" + + "github.com/gin-gonic/gin" +) + +type ImportHandler struct { + importService *services.Import_Service +} + +func NewImportHandler(i *services.Import_Service) *ImportHandler { + return &ImportHandler{ + importService: i, + } +} + +func (h *ImportHandler) ImportPropertyTypeGroups(ctx *gin.Context) { + // Implement the logic to import property types groups here +} diff --git a/buildtime/internal/handler/mainHandler.go b/buildtime/internal/handler/mainHandler.go new file mode 100644 index 0000000..26a74d5 --- /dev/null +++ b/buildtime/internal/handler/mainHandler.go @@ -0,0 +1,21 @@ +package handlers + +import ( + "SimplyCPQ2/internal/services" + + "github.com/gin-gonic/gin" +) + +type MainHandler struct { + propertyTypeService *services.PropertyTypeService +} + +func NewMainHandler(p *services.PropertyTypeService) *MainHandler { + return &MainHandler{ + propertyTypeService: p, + } +} + +func (h *MainHandler) Home(ctx *gin.Context) { + // Implement the logic for the main handler's home endpoint here +} diff --git a/buildtime/internal/handler/propertyTypeHandler.go b/buildtime/internal/handler/propertyTypeHandler.go new file mode 100644 index 0000000..142504b --- /dev/null +++ b/buildtime/internal/handler/propertyTypeHandler.go @@ -0,0 +1,21 @@ +package handlers + +import ( + "SimplyCPQ2/internal/services" + + "github.com/gin-gonic/gin" +) + +type PropertyTypeHandler struct { + propertyTypeService *services.PropertyTypeService +} + +func NewPropertyTypeHandler(propertyTypeService *services.PropertyTypeService) *PropertyTypeHandler { + return &PropertyTypeHandler{ + propertyTypeService: propertyTypeService, + } +} + +func (h *PropertyTypeHandler) GetRootPropertyTypeGroups(ctx *gin.Context) { + // Implement the logic to get root property types groups here +} diff --git a/buildtime/internal/models/product.go b/buildtime/internal/models/product.go new file mode 100644 index 0000000..3aba54f --- /dev/null +++ b/buildtime/internal/models/product.go @@ -0,0 +1,9 @@ +package models + +type Product struct { + Id int + Name string + Description string + ImageURL string + Properties []Property +} diff --git a/buildtime/internal/models/productGroup.go b/buildtime/internal/models/productGroup.go new file mode 100644 index 0000000..eafb6d1 --- /dev/null +++ b/buildtime/internal/models/productGroup.go @@ -0,0 +1,9 @@ +package models + +type ProductGroup struct { + Id int `json:"id,omitempty"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + ImageURL string `json:"ImageURL,omitempty"` + Properties []Property `json:"properties,omitempty"` +} diff --git a/buildtime/internal/models/property.go b/buildtime/internal/models/property.go new file mode 100644 index 0000000..d9765cf --- /dev/null +++ b/buildtime/internal/models/property.go @@ -0,0 +1,13 @@ +package models + +type Property struct { + Id int `json:"id,omitempty"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + ImageURL string `json:"ImageURL,omitempty"` + //Property_Type *Property_Type `json:"property_type,omitempty"` + Parent_Property *Property `json:"parent_property,omitempty"` + Products_Group *ProductGroup `json:"products_group,omitempty"` + Product *Product `json:"products,omitempty"` + Values []Value `json:"values,omitempty"` +} diff --git a/buildtime/internal/models/propertyType.go b/buildtime/internal/models/propertyType.go new file mode 100644 index 0000000..4944a22 --- /dev/null +++ b/buildtime/internal/models/propertyType.go @@ -0,0 +1,9 @@ +package models + +type Property_Type struct { + Id int `json:"id,omitempty"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + ImageURL string `json:"ImageURL,omitempty"` + Property Property `json:"property"` +} diff --git a/buildtime/internal/models/propertyTypeGroup.go b/buildtime/internal/models/propertyTypeGroup.go new file mode 100644 index 0000000..f53aee2 --- /dev/null +++ b/buildtime/internal/models/propertyTypeGroup.go @@ -0,0 +1,10 @@ +package models + +type PropertyTypeGroup struct { + Id int `json:"id,omitempty"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + ImageURL string `json:"ImageURL,omitempty"` + PropertyTypes []Property_Type `json:"PropertyTypes,omitempty"` + PropertyTypeGroups []PropertyTypeGroup `json:"PropertyTypeGroups,omitempty"` +} diff --git a/buildtime/internal/models/value.go b/buildtime/internal/models/value.go new file mode 100644 index 0000000..be7644b --- /dev/null +++ b/buildtime/internal/models/value.go @@ -0,0 +1,7 @@ +package models + +type Value struct { + Id int `json:"id,omitempty"` + Value string `json:"value"` + ImageURL string `json:"ImageURL,omitempty"` +} diff --git a/buildtime/internal/repositories/importRepository.go b/buildtime/internal/repositories/importRepository.go new file mode 100644 index 0000000..44b9e0f --- /dev/null +++ b/buildtime/internal/repositories/importRepository.go @@ -0,0 +1,23 @@ +package repositories + +import ( + "database/sql" +) + +type ImportRepository struct { + db *sql.DB + vr *ValueRepository + pt *PropertyTypeRepository + ptg *PropertyTypeGroupRepository + pr *PropertyRepository +} + +func NewImportRepository(db *sql.DB, vr *ValueRepository, pt *PropertyTypeRepository, ptg *PropertyTypeGroupRepository, pr *PropertyRepository) *ImportRepository { + return &ImportRepository{ + db: db, + vr: vr, + pt: pt, + ptg: ptg, + pr: pr, + } +} diff --git a/buildtime/internal/repositories/propertyRepository.go b/buildtime/internal/repositories/propertyRepository.go new file mode 100644 index 0000000..c50b5ba --- /dev/null +++ b/buildtime/internal/repositories/propertyRepository.go @@ -0,0 +1,17 @@ +package repositories + +import ( + "database/sql" +) + +type PropertyRepository struct { + db *sql.DB + vr *ValueRepository +} + +func NewPropertyRepository(db *sql.DB, valueRepository *ValueRepository) *PropertyRepository { + return &PropertyRepository{ + db: db, + vr: valueRepository, + } +} diff --git a/buildtime/internal/repositories/propertyTypeGroupRepository.go b/buildtime/internal/repositories/propertyTypeGroupRepository.go new file mode 100644 index 0000000..76173ca --- /dev/null +++ b/buildtime/internal/repositories/propertyTypeGroupRepository.go @@ -0,0 +1,17 @@ +package repositories + +import ( + "database/sql" +) + +type PropertyTypeGroupRepository struct { + db *sql.DB + PropertyTypeRepository *PropertyTypeRepository +} + +func NewPropertyTypesGroupRepository(db *sql.DB, PropertyTypeRepository *PropertyTypeRepository) *PropertyTypeGroupRepository { + return &PropertyTypeGroupRepository{ + db: db, + PropertyTypeRepository: PropertyTypeRepository, + } +} diff --git a/buildtime/internal/repositories/propertyTypeRepository.go b/buildtime/internal/repositories/propertyTypeRepository.go new file mode 100644 index 0000000..2ea6b73 --- /dev/null +++ b/buildtime/internal/repositories/propertyTypeRepository.go @@ -0,0 +1,17 @@ +package repositories + +import ( + "database/sql" +) + +type PropertyTypeRepository struct { + db *sql.DB + pr *PropertyRepository +} + +func NewPropertyTypeRepository(db *sql.DB, propertyRepository *PropertyRepository) *PropertyTypeRepository { + return &PropertyTypeRepository{ + db: db, + pr: propertyRepository, + } +} diff --git a/buildtime/internal/repositories/value_repository.go b/buildtime/internal/repositories/value_repository.go new file mode 100644 index 0000000..9abbe12 --- /dev/null +++ b/buildtime/internal/repositories/value_repository.go @@ -0,0 +1,15 @@ +package repositories + +import ( + "database/sql" +) + +type ValueRepository struct { + db *sql.DB +} + +func NewValueRepository(db *sql.DB) *ValueRepository { + return &ValueRepository{ + db: db, + } +} diff --git a/buildtime/internal/services/import_service.go b/buildtime/internal/services/import_service.go new file mode 100644 index 0000000..015d2f4 --- /dev/null +++ b/buildtime/internal/services/import_service.go @@ -0,0 +1,15 @@ +package services + +import ( + "SimplyCPQ2/internal/repositories" +) + +type Import_Service struct { + importRepository *repositories.ImportRepository +} + +func NewImportService(importRepository *repositories.ImportRepository) *Import_Service { + return &Import_Service{ + importRepository: importRepository, + } +} diff --git a/buildtime/internal/services/propertyTypeService.go b/buildtime/internal/services/propertyTypeService.go new file mode 100644 index 0000000..a5cd082 --- /dev/null +++ b/buildtime/internal/services/propertyTypeService.go @@ -0,0 +1,18 @@ +package services + +import ( + "SimplyCPQ2/internal/repositories" +) + +type PropertyTypeService struct { + // Add any dependencies or fields needed for the service here + PropertyTypes_Repository *repositories.PropertyTypeRepository + PropertyTypeGroups_Repository *repositories.PropertyTypeGroupRepository +} + +func NewPropertyTypeService(propertyTypesRepo *repositories.PropertyTypeRepository, propertyTypesGroupsRepo *repositories.PropertyTypeGroupRepository) *PropertyTypeService { + return &PropertyTypeService{ + PropertyTypes_Repository: propertyTypesRepo, + PropertyTypeGroups_Repository: propertyTypesGroupsRepo, + } +} diff --git a/buildtime/static/css/scpq_content.css b/buildtime/static/css/scpq_content.css new file mode 100644 index 0000000..da429ce --- /dev/null +++ b/buildtime/static/css/scpq_content.css @@ -0,0 +1,103 @@ +/** layout Main Bereich */ + +main { + padding: var(--main-content-padding); + margin: 16px; + height: calc(100vh - var(--header-height) - var(--footer-height) - 4 * var(--main-content-padding) - 64px); + /* 32px für die Margen */ + overflow-y: auto; + /* Scrollbar, wenn der Inhalt zu groß ist */ + border: 1px solid var(--secondary-color); + border-radius: var(--box-border-radius); +} + +.editor-container { + height: calc(100vh - var(--header-height) - var(--footer-height) - 4 * var(--main-content-padding) - 132px); + display: flex; + flex-direction: row; + /* Elemente horizontal anordnen */ + gap: 16px; + /* Abstand zwischen den Elementen */ + padding: 16px; + /* Innenabstand */ + border-radius: var(--box-border-radius); + background-color: var(--data-background-color); +} + +.editor-view { + margin-top: 8px; + padding: 8px; + border: 1px solid var(--primary-color); + border-radius: var(--box-border-radius); +} + +/** tree components */ +.node { + justify-content: center; + list-style-type: none; + /* Entfernt die Standard-Aufzählungszeichen */ + padding-left: 20px; + /* Einrückung für die Unterlisten */ +} + +.node img { + height: 12px; + /* Höhe des Bildes */ + margin-right: 2px; + /* Abstand zwischen Bild und Text */ +} + +.leaf { + padding-left: 20px; + /* Einrückung für Blätter */ + color: var(--primary-color); + font-weight: lighter; +} + + +/* form styles */ +.cform { + display: flex; + flex-direction: column; + color: var(--primary-color); + gap: 8px; + padding: 16px; +} + +.cform label { + padding-top: 8px; +} + +.cform input[type="text"], +.cform textarea { + padding: 4px; + border: 1px solid var(--primary-color); + border-radius: var(--box-border-radius); +} + +/* Style für allgemeine Elemente */ +.box { + border: 1px solid var(--primary-color); + border-radius: var(--box-border-radius); +} + +.button_row { + display: flex; + flex-direction: row; + justify-content: flex-end; + gap: 8px; + margin-top: 8px; +} +.button_row button { + padding: 4px 8px; + border: 1px solid var(--primary-color); + border-radius: var(--box-border-radius); + background-color: var(--data-background-color); + cursor: pointer; + display: inline-flex; + justify-content: center; +} + +.mt-16 { + margin-top: 16px; +} \ No newline at end of file diff --git a/buildtime/static/css/scpq_footer.css b/buildtime/static/css/scpq_footer.css new file mode 100644 index 0000000..38c4c71 --- /dev/null +++ b/buildtime/static/css/scpq_footer.css @@ -0,0 +1,11 @@ +/** footer styles */ + +footer { + height: var(--footer-height); + background-color: var(--primary-color); + border: 1px solid var(--secondary-color); + border-radius: var(--box-border-radius); + color: var(--secondary-font-color); + margin: 16px; + padding: 10px; +} diff --git a/buildtime/static/css/scpq_global.css b/buildtime/static/css/scpq_global.css new file mode 100644 index 0000000..e301b3f --- /dev/null +++ b/buildtime/static/css/scpq_global.css @@ -0,0 +1,43 @@ +/** global styles */ + +/* globale Variablen */ +:root { + /* colors */ + --primary-color: #1976D2; + --secondary-color: #d27519; + --font-color: #333333; + --secondary-font-color: #f3f5f4; + --background-color: #f3f5f4; + --highlight-color: #f0f0f0; + --error-color: #ff4d4f; + --success-color: #52c41a; + --data-background-color: #ffffff; + + /* Dimensions */ + --header-height: 32px; + --footer-height: 32px; + --main-content-padding: 20px; + --box-border-radius: 10px; +} + + +/* 1. Schriftart definieren */ +@font-face { + font-family: 'Inter'; /* Name, den du später im CSS verwendest */ + src: url('/static/fonts/Inter.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +* { + margin: 0; + padding: 0; +} + +/* 2. Schriftart anwenden und Ränder entfernen*/ +body { + font-family: 'Inter', Arial, sans-serif; + background-color: var(--background-color); + color: var(--font-color); +} + diff --git a/buildtime/static/css/scpq_header.css b/buildtime/static/css/scpq_header.css new file mode 100644 index 0000000..6711155 --- /dev/null +++ b/buildtime/static/css/scpq_header.css @@ -0,0 +1,48 @@ +/** Header and included nav styles */ + +/* Style für Header, Navigation, Main Content, Aside und Footer */ +header { + display: flex; + justify-content: flex-start; + align-items: center; + + height: var(--header-height); + background-color: var(--primary-color); + border: 1px solid var(--secondary-color); + border-radius: var(--box-border-radius); + color: var(--secondary-font-color); + margin: 16px; + padding: 10px; +} + +header img { + height: calc(var(--header-height) - 5px); /* Adjust the image height to fit within the header */ + margin-left: 10px; /* Space between the image and the header text */ + padding-right: 16px; /* Space between the image and the header text */ +} + +header p { + font-weight: 100; + margin-left: 16px; + padding-right: 16px; +} + +nav { + margin: 16px; + padding: 10px; +} + +nav ul { + list-style-type: none; /* Entfernt die Standard-Aufzählungszeichen */ + display: flex; /* Macht die Liste horizontal */ + gap: 20px; /* Abstand zwischen den Listenelementen */ +} + +nav ul li a { + text-decoration: none; /* Entfernt die Unterstreichung */ + color: var(--secondary-font-color); /* Setzt die Schriftfarbe */ +} +nav ul li a:hover { + text-decoration: underline; /* Unterstreichung beim Überfahren mit der Maus */ + font-weight: bold; /* Macht den Text fett */ +} diff --git a/buildtime/static/fonts/Inter.ttf b/buildtime/static/fonts/Inter.ttf new file mode 100644 index 0000000..e31b51e Binary files /dev/null and b/buildtime/static/fonts/Inter.ttf differ diff --git a/buildtime/static/images/SimplyCPQ_Logo.svg b/buildtime/static/images/SimplyCPQ_Logo.svg new file mode 100644 index 0000000..3088fe1 --- /dev/null +++ b/buildtime/static/images/SimplyCPQ_Logo.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/buildtime/static/images/property_type.svg b/buildtime/static/images/property_type.svg new file mode 100644 index 0000000..963c2d6 --- /dev/null +++ b/buildtime/static/images/property_type.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/buildtime/static/images/property_type_group.svg b/buildtime/static/images/property_type_group.svg new file mode 100644 index 0000000..a2f71ec --- /dev/null +++ b/buildtime/static/images/property_type_group.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/buildtime/static/images/screens/dummy.webp b/buildtime/static/images/screens/dummy.webp new file mode 100644 index 0000000..e4782c9 Binary files /dev/null and b/buildtime/static/images/screens/dummy.webp differ diff --git a/buildtime/static/images/users/tsindt.png b/buildtime/static/images/users/tsindt.png new file mode 100644 index 0000000..54f4d57 Binary files /dev/null and b/buildtime/static/images/users/tsindt.png differ diff --git a/buildtime/static/js/htmx.js b/buildtime/static/js/htmx.js new file mode 100644 index 0000000..4d76457 --- /dev/null +++ b/buildtime/static/js/htmx.js @@ -0,0 +1,5342 @@ +var htmx = (function() { + 'use strict' + + // Public API + const htmx = { + // Tsc madness here, assigning the functions directly results in an invalid TypeScript output, but reassigning is fine + /* Event processing */ + /** @type {typeof onLoadHelper} */ + onLoad: null, + /** @type {typeof processNode} */ + process: null, + /** @type {typeof addEventListenerImpl} */ + on: null, + /** @type {typeof removeEventListenerImpl} */ + off: null, + /** @type {typeof triggerEvent} */ + trigger: null, + /** @type {typeof ajaxHelper} */ + ajax: null, + /* DOM querying helpers */ + /** @type {typeof find} */ + find: null, + /** @type {typeof findAll} */ + findAll: null, + /** @type {typeof closest} */ + closest: null, + /** + * Returns the input values that would resolve for a given element via the htmx value resolution mechanism + * + * @see https://htmx.org/api/#values + * + * @param {Element} elt the element to resolve values on + * @param {HttpVerb} type the request type (e.g. **get** or **post**) non-GET's will include the enclosing form of the element. Defaults to **post** + * @returns {Object} + */ + values: function(elt, type) { + const inputValues = getInputValues(elt, type || 'post') + return inputValues.values + }, + /* DOM manipulation helpers */ + /** @type {typeof removeElement} */ + remove: null, + /** @type {typeof addClassToElement} */ + addClass: null, + /** @type {typeof removeClassFromElement} */ + removeClass: null, + /** @type {typeof toggleClassOnElement} */ + toggleClass: null, + /** @type {typeof takeClassForElement} */ + takeClass: null, + /** @type {typeof swap} */ + swap: null, + /* Extension entrypoints */ + /** @type {typeof defineExtension} */ + defineExtension: null, + /** @type {typeof removeExtension} */ + removeExtension: null, + /* Debugging */ + /** @type {typeof logAll} */ + logAll: null, + /** @type {typeof logNone} */ + logNone: null, + /* Debugging */ + /** + * The logger htmx uses to log with + * + * @see https://htmx.org/api/#logger + */ + logger: null, + /** + * A property holding the configuration htmx uses at runtime. + * + * Note that using a [meta tag](https://htmx.org/docs/#config) is the preferred mechanism for setting these properties. + * + * @see https://htmx.org/api/#config + */ + config: { + /** + * Whether to use history. + * @type boolean + * @default true + */ + historyEnabled: true, + /** + * The number of pages to keep in **sessionStorage** for history support. + * @type number + * @default 10 + */ + historyCacheSize: 10, + /** + * @type boolean + * @default false + */ + refreshOnHistoryMiss: false, + /** + * The default swap style to use if **[hx-swap](https://htmx.org/attributes/hx-swap)** is omitted. + * @type HtmxSwapStyle + * @default 'innerHTML' + */ + defaultSwapStyle: 'innerHTML', + /** + * The default delay between receiving a response from the server and doing the swap. + * @type number + * @default 0 + */ + defaultSwapDelay: 0, + /** + * The default delay between completing the content swap and settling attributes. + * @type number + * @default 20 + */ + defaultSettleDelay: 20, + /** + * If true, htmx will inject a small amount of CSS into the page to make indicators invisible unless the **htmx-indicator** class is present. + * @type boolean + * @default true + */ + includeIndicatorStyles: true, + /** + * The class to place on indicators when a request is in flight. + * @type string + * @default 'htmx-indicator' + */ + indicatorClass: 'htmx-indicator', + /** + * The class to place on triggering elements when a request is in flight. + * @type string + * @default 'htmx-request' + */ + requestClass: 'htmx-request', + /** + * The class to temporarily place on elements that htmx has added to the DOM. + * @type string + * @default 'htmx-added' + */ + addedClass: 'htmx-added', + /** + * The class to place on target elements when htmx is in the settling phase. + * @type string + * @default 'htmx-settling' + */ + settlingClass: 'htmx-settling', + /** + * The class to place on target elements when htmx is in the swapping phase. + * @type string + * @default 'htmx-swapping' + */ + swappingClass: 'htmx-swapping', + /** + * Allows the use of eval-like functionality in htmx, to enable **hx-vars**, trigger conditions & script tag evaluation. Can be set to **false** for CSP compatibility. + * @type boolean + * @default true + */ + allowEval: true, + /** + * If set to false, disables the interpretation of script tags. + * @type boolean + * @default true + */ + allowScriptTags: true, + /** + * If set, the nonce will be added to inline scripts. + * @type string + * @default '' + */ + inlineScriptNonce: '', + /** + * If set, the nonce will be added to inline styles. + * @type string + * @default '' + */ + inlineStyleNonce: '', + /** + * The attributes to settle during the settling phase. + * @type string[] + * @default ['class', 'style', 'width', 'height'] + */ + attributesToSettle: ['class', 'style', 'width', 'height'], + /** + * Allow cross-site Access-Control requests using credentials such as cookies, authorization headers or TLS client certificates. + * @type boolean + * @default false + */ + withCredentials: false, + /** + * @type number + * @default 0 + */ + timeout: 0, + /** + * The default implementation of **getWebSocketReconnectDelay** for reconnecting after unexpected connection loss by the event code **Abnormal Closure**, **Service Restart** or **Try Again Later**. + * @type {'full-jitter' | ((retryCount:number) => number)} + * @default "full-jitter" + */ + wsReconnectDelay: 'full-jitter', + /** + * The type of binary data being received over the WebSocket connection + * @type BinaryType + * @default 'blob' + */ + wsBinaryType: 'blob', + /** + * @type string + * @default '[hx-disable], [data-hx-disable]' + */ + disableSelector: '[hx-disable], [data-hx-disable]', + /** + * @type {'auto' | 'instant' | 'smooth'} + * @default 'instant' + */ + scrollBehavior: 'instant', + /** + * If the focused element should be scrolled into view. + * @type boolean + * @default false + */ + defaultFocusScroll: false, + /** + * If set to true htmx will include a cache-busting parameter in GET requests to avoid caching partial responses by the browser + * @type boolean + * @default false + */ + getCacheBusterParam: false, + /** + * If set to true, htmx will use the View Transition API when swapping in new content. + * @type boolean + * @default false + */ + globalViewTransitions: false, + /** + * htmx will format requests with these methods by encoding their parameters in the URL, not the request body + * @type {(HttpVerb)[]} + * @default ['get', 'delete'] + */ + methodsThatUseUrlParams: ['get', 'delete'], + /** + * If set to true, disables htmx-based requests to non-origin hosts. + * @type boolean + * @default false + */ + selfRequestsOnly: true, + /** + * If set to true htmx will not update the title of the document when a title tag is found in new content + * @type boolean + * @default false + */ + ignoreTitle: false, + /** + * Whether the target of a boosted element is scrolled into the viewport. + * @type boolean + * @default true + */ + scrollIntoViewOnBoost: true, + /** + * The cache to store evaluated trigger specifications into. + * You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy) + * @type {Object|null} + * @default null + */ + triggerSpecsCache: null, + /** @type boolean */ + disableInheritance: false, + /** @type HtmxResponseHandlingConfig[] */ + responseHandling: [ + { code: '204', swap: false }, + { code: '[23]..', swap: true }, + { code: '[45]..', swap: false, error: true } + ], + /** + * Whether to process OOB swaps on elements that are nested within the main response element. + * @type boolean + * @default true + */ + allowNestedOobSwaps: true, + /** + * Whether to treat history cache miss full page reload requests as a "HX-Request" by returning this response header + * This should always be disabled when using HX-Request header to optionally return partial responses + * @type boolean + * @default true + */ + historyRestoreAsHxRequest: true, + /** + * Whether to report input validation errors to the end user and update focus to the first input that fails validation. + * This should always be enabled as this matches default browser form submit behaviour + * @type boolean + * @default false + */ + reportValidityOfForms: false + }, + /** @type {typeof parseInterval} */ + parseInterval: null, + /** + * proxy of window.location used for page reload functions + * @type location + */ + location, + /** @type {typeof internalEval} */ + _: null, + version: '2.0.10' + } + // Tsc madness part 2 + htmx.onLoad = onLoadHelper + htmx.process = processNode + htmx.on = addEventListenerImpl + htmx.off = removeEventListenerImpl + htmx.trigger = triggerEvent + htmx.ajax = ajaxHelper + htmx.find = find + htmx.findAll = findAll + htmx.closest = closest + htmx.remove = removeElement + htmx.addClass = addClassToElement + htmx.removeClass = removeClassFromElement + htmx.toggleClass = toggleClassOnElement + htmx.takeClass = takeClassForElement + htmx.swap = swap + htmx.defineExtension = defineExtension + htmx.removeExtension = removeExtension + htmx.logAll = logAll + htmx.logNone = logNone + htmx.parseInterval = parseInterval + htmx._ = internalEval + + const internalAPI = { + addTriggerHandler, + bodyContains, + canAccessLocalStorage, + findThisElement, + filterValues, + swap, + hasAttribute, + getAttributeValue, + getClosestAttributeValue, + getClosestMatch, + getExpressionVars, + getHeaders, + getInputValues, + getInternalData, + getSwapSpecification, + getTriggerSpecs, + getTarget, + makeFragment, + mergeObjects, + makeSettleInfo, + oobSwap, + querySelectorExt, + settleImmediately, + shouldCancel, + triggerEvent, + triggerErrorEvent, + withExtensions + } + + const VERBS = ['get', 'post', 'put', 'delete', 'patch'] + const VERB_SELECTOR = VERBS.map(function(verb) { + return '[hx-' + verb + '], [data-hx-' + verb + ']' + }).join(', ') + + //= =================================================================== + // Utilities + //= =================================================================== + + /** + * Parses an interval string consistent with the way htmx does. Useful for plugins that have timing-related attributes. + * + * Caution: Accepts an int followed by either **s** or **ms**. All other values use **parseFloat** + * + * @see https://htmx.org/api/#parseInterval + * + * @param {string} str timing string + * @returns {number|undefined} + */ + function parseInterval(str) { + if (str == undefined) { + return undefined + } + + let interval = NaN + if (str.slice(-2) == 'ms') { + interval = parseFloat(str.slice(0, -2)) + } else if (str.slice(-1) == 's') { + interval = parseFloat(str.slice(0, -1)) * 1000 + } else if (str.slice(-1) == 'm') { + interval = parseFloat(str.slice(0, -1)) * 1000 * 60 + } else { + interval = parseFloat(str) + } + return isNaN(interval) ? undefined : interval + } + + /** + * @param {Node} elt + * @param {string} name + * @returns {(string | null)} + */ + function getRawAttribute(elt, name) { + return elt instanceof Element && elt.getAttribute(name) + } + + /** + * @param {Element} elt + * @param {string} qualifiedName + * @returns {boolean} + */ + // resolve with both hx and data-hx prefixes + function hasAttribute(elt, qualifiedName) { + return !!elt.hasAttribute && (elt.hasAttribute(qualifiedName) || + elt.hasAttribute('data-' + qualifiedName)) + } + + /** + * + * @param {Node} elt + * @param {string} qualifiedName + * @returns {(string | null)} + */ + function getAttributeValue(elt, qualifiedName) { + return getRawAttribute(elt, qualifiedName) || getRawAttribute(elt, 'data-' + qualifiedName) + } + + /** + * @param {Node} elt + * @returns {Node | null} + */ + function parentElt(elt) { + const parent = elt.parentElement + if (!parent && elt.parentNode instanceof ShadowRoot) return elt.parentNode + return parent + } + + /** + * @returns {Document} + */ + function getDocument() { + return document + } + + /** + * @param {Node} elt + * @param {boolean} global + * @returns {Node|Document} + */ + function getRootNode(elt, global) { + return elt.getRootNode ? elt.getRootNode({ composed: global }) : getDocument() + } + + /** + * @param {Node} elt + * @param {(e:Node) => boolean} condition + * @returns {Node | null} + */ + function getClosestMatch(elt, condition) { + while (elt && !condition(elt)) { + elt = parentElt(elt) + } + + return elt || null + } + + /** + * @param {Element} initialElement + * @param {Element} ancestor + * @param {string} attributeName + * @returns {string|null} + */ + function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName) { + const attributeValue = getAttributeValue(ancestor, attributeName) + const disinherit = getAttributeValue(ancestor, 'hx-disinherit') + var inherit = getAttributeValue(ancestor, 'hx-inherit') + if (initialElement !== ancestor) { + if (htmx.config.disableInheritance) { + if (inherit && (inherit === '*' || inherit.split(' ').indexOf(attributeName) >= 0)) { + return attributeValue + } else { + return null + } + } + if (disinherit && (disinherit === '*' || disinherit.split(' ').indexOf(attributeName) >= 0)) { + return 'unset' + } + } + return attributeValue + } + + /** + * @param {Element} elt + * @param {string} attributeName + * @returns {string | null} + */ + function getClosestAttributeValue(elt, attributeName) { + let closestAttr = null + getClosestMatch(elt, function(e) { + return !!(closestAttr = getAttributeValueWithDisinheritance(elt, asElement(e), attributeName)) + }) + if (closestAttr !== 'unset') { + return closestAttr + } + } + + /** + * @param {Node} elt + * @param {string} selector + * @returns {boolean} + */ + function matches(elt, selector) { + return elt instanceof Element && elt.matches(selector) + } + + /** + * @param {string} str + * @returns {string} + */ + function getStartTag(str) { + const tagMatcher = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i + const match = tagMatcher.exec(str) + if (match) { + return match[1].toLowerCase() + } else { + return '' + } + } + + /** + * @param {string} resp + * @returns {Document} + */ + function parseHTML(resp) { + if ('parseHTMLUnsafe' in Document) { + return Document.parseHTMLUnsafe(resp) + } + const parser = new DOMParser() + return parser.parseFromString(resp, 'text/html') + } + + /** + * @param {DocumentFragment} fragment + * @param {Node} elt + */ + function takeChildrenFor(fragment, elt) { + while (elt.childNodes.length > 0) { + fragment.append(elt.childNodes[0]) + } + } + + /** + * @param {HTMLScriptElement} script + * @returns {HTMLScriptElement} + */ + function duplicateScript(script) { + const newScript = getDocument().createElement('script') + forEach(script.attributes, function(attr) { + newScript.setAttribute(attr.name, attr.value) + }) + newScript.textContent = script.textContent + newScript.async = false + if (htmx.config.inlineScriptNonce) { + newScript.nonce = htmx.config.inlineScriptNonce + } + return newScript + } + + /** + * @param {HTMLScriptElement} script + * @returns {boolean} + */ + function isJavaScriptScriptNode(script) { + return script.matches('script') && (script.type === 'text/javascript' || script.type === 'module' || script.type === '') + } + + /** + * we have to make new copies of script tags that we are going to insert because + * SOME browsers (not saying who, but it involves an element and an animal) don't + * execute scripts created in