diff --git a/Makefile b/Makefile index 5bc3984..2c3d03b 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ export GOOSE_MIGRATION_DIR=db/migrations export REDMAGE_WEB_DEPENDENCIES_HTMX_VERSION=$(shell echo "$${REDMAGE_WEB_DEPENDENCIES_HTMX_VERSION:-1.9.12}") export REDMAGE_WEB_DEPENDENCIES_DAYJS_VERSION=$(shell echo "$${REDMAGE_WEB_DEPENDENCIES_DAYJS_VERSION:-1.11.10}") +export REDMAGE_WEB_DEPENDENCIES_ALPINEJS_VERSION=$(shell echo "$${REDMAGE_WEB_DEPENDENCIES_ALPINEJS_VERSION:-3.13.10}") start: dev-dependencies @tailwindcss -i views/style.css -o public/style.css --watch & @@ -62,6 +63,11 @@ build-dependencies: echo "Dayjs Timezone ${REDMAGE_WEB_DEPENDENCIES_DAYJS_VERSION} plugin not found, installing it" curl -o public/dayjs-timezone-${REDMAGE_WEB_DEPENDENCIES_DAYJS_VERSION}.min.js https://cdnjs.cloudflare.com/ajax/libs/dayjs/${REDMAGE_WEB_DEPENDENCIES_DAYJS_VERSION}/plugin/timezone.min.js fi + @if [ ! -f "public/alpinejs-${REDMAGE_WEB_DEPENDENCIES_ALPINEJS_VERSION}.min.js" ]; then + mkdir -p public + echo "Alpinejs ${REDMAGE_WEB_DEPENDENCIES_ALPINEJS_VERSION} not found, installing it" + curl -o public/alpinejs-${REDMAGE_WEB_DEPENDENCIES_ALPINEJS_VERSION}.min.js https://cdn.jsdelivr.net/npm/alpinejs@${REDMAGE_WEB_DEPENDENCIES_ALPINEJS_VERSION}/dist/cdn.min.js + fi build: build-dependencies prepare go build -o redmage diff --git a/api/devices_validate_slug.go b/api/devices_validate_slug.go new file mode 100644 index 0000000..9a443a8 --- /dev/null +++ b/api/devices_validate_slug.go @@ -0,0 +1,19 @@ +package api + +import ( + "context" + + "github.com/tigorlazuardi/redmage/models" + "github.com/tigorlazuardi/redmage/pkg/errs" +) + +func (api *API) DevicesValidateSlug(ctx context.Context, slug string) (exist bool, err error) { + ctx, span := tracer.Start(ctx, "*API.DevicesValidateSlug") + defer span.End() + + exist, err = models.Devices.Query(ctx, api.db, models.SelectWhere.Devices.Slug.EQ(slug)).Exists() + if err != nil { + return exist, errs.Wrapw(err, "failed to check device slug existence", "slug", slug) + } + return exist, err +} diff --git a/server/routes/device_validate_slug.go b/server/routes/device_validate_slug.go new file mode 100644 index 0000000..4f70354 --- /dev/null +++ b/server/routes/device_validate_slug.go @@ -0,0 +1,49 @@ +package routes + +import ( + "net/http" + + "github.com/tigorlazuardi/redmage/pkg/errs" + "github.com/tigorlazuardi/redmage/pkg/log" + "github.com/tigorlazuardi/redmage/views/devicesview/adddevice" +) + +func (routes *Routes) DevicesValidateSlugHTMX(rw http.ResponseWriter, req *http.Request) { + ctx, span := tracer.Start(req.Context(), "*Routes.ValidateSlugHTMX") + defer span.End() + + var data adddevice.SlugInputData + data.Value = req.FormValue("slug") + if data.Value == "" { + if err := adddevice.SlugInput(data).Render(ctx, rw); err != nil { + log.New(ctx).Err(err).Error("failed to render slug input") + } + return + } + + exist, err := routes.API.DevicesValidateSlug(ctx, data.Value) + if err != nil { + log.New(ctx).Err(err).Error("failed to validate slug") + code, message := errs.HTTPMessage(err) + rw.WriteHeader(code) + data.Error = message + if err := adddevice.SlugInput(data).Render(ctx, rw); err != nil { + log.New(ctx).Err(err).Error("failed to render slug input") + } + return + } + + if exist { + data.Error = "Device with this identifier already exist" + rw.WriteHeader(http.StatusConflict) + if err := adddevice.SlugInput(data).Render(ctx, rw); err != nil { + log.New(ctx).Err(err).Error("failed to render slug input") + } + return + } + data.Valid = "Identifier is available" + + if err := adddevice.SlugInput(data).Render(ctx, rw); err != nil { + log.New(ctx).Err(err).Error("failed to render slug input") + } +} diff --git a/server/routes/routes.go b/server/routes/routes.go index 49520ef..a864a8e 100644 --- a/server/routes/routes.go +++ b/server/routes/routes.go @@ -62,6 +62,8 @@ func (routes *Routes) registerHTMXRoutes(router chi.Router) { router.Post("/subreddits/start", routes.SubredditStartDownloadHTMX) router.Post("/subreddits/check", routes.SubredditCheckHTMX) router.Get("/subreddits/validate/schedule", routes.SubredditValidateScheduleHTMX) + + router.Post("/devices/add/validate/slug", routes.DevicesValidateSlugHTMX) } func (routes *Routes) registerWWWRoutes(router chi.Router) { diff --git a/views/components/alpine.templ b/views/components/alpine.templ new file mode 100644 index 0000000..b5e9198 --- /dev/null +++ b/views/components/alpine.templ @@ -0,0 +1,8 @@ +package components + +import "github.com/tigorlazuardi/redmage/views" +import "fmt" + +templ AlpineJS(c *views.Context) { + +} diff --git a/views/components/head.templ b/views/components/head.templ index f42bd72..e935604 100644 --- a/views/components/head.templ +++ b/views/components/head.templ @@ -13,6 +13,7 @@ templ Head(vc *views.Context, extras ...templ.Component) { @Dayjs(vc) @HTMX(vc) + @AlpineJS(vc) if vc.Config.Bool("http.hotreload") { } diff --git a/views/devicesview/adddevice/name_input.templ b/views/devicesview/adddevice/name_input.templ new file mode 100644 index 0000000..ca20b39 --- /dev/null +++ b/views/devicesview/adddevice/name_input.templ @@ -0,0 +1,39 @@ +package adddevice + +import "github.com/tigorlazuardi/redmage/views/utils" +import "fmt" + +type NameInputData struct { + Error string + Value string +} + +templ NameInput(data NameInputData) { + +} diff --git a/views/devicesview/adddevice/slug_input.templ b/views/devicesview/adddevice/slug_input.templ new file mode 100644 index 0000000..c85daf6 --- /dev/null +++ b/views/devicesview/adddevice/slug_input.templ @@ -0,0 +1,58 @@ +package adddevice + +import "github.com/tigorlazuardi/redmage/views/utils" +import "fmt" + +type SlugInputData struct { + Error string + Value string + Valid string + HXSwapOOB bool +} + +templ SlugInput(data SlugInputData) { + +} diff --git a/views/devicesview/adddevice/view.templ b/views/devicesview/adddevice/view.templ index 72e2449..9950c85 100644 --- a/views/devicesview/adddevice/view.templ +++ b/views/devicesview/adddevice/view.templ @@ -17,6 +17,10 @@ templ Content(c *views.Context) { @components.Container() {

Add Device

+
+ @NameInput(NameInputData{}) + @SlugInput(SlugInputData{}) +
} }