diff --git a/admin/.env.example b/admin/.env.example new file mode 100644 index 0000000..706fd43 --- /dev/null +++ b/admin/.env.example @@ -0,0 +1,13 @@ +# NestQuest Admin PWA — runtime configuration (Vite env). +# Copy to .env (or .env.local) and fill in real values. NEVER commit .env. + +# Authentik OIDC provider. Defaults to the issuer below when unset. +VITE_AUTHENTIK_ISSUER=https://auth.cubecraftlabs.com/application/o/nestquest/ + +# REQUIRED. OAuth2/OIDC client id of the PUBLIC (PKCE) Authentik application. +# No default: the app shows a configuration error screen when this is missing. +# Never set a client_secret — this is a browser SPA using PKCE only. +VITE_AUTHENTIK_CLIENT_ID= + +# NestQuest API base URL. +VITE_API_BASE_URL=https://nestquest.cubecraftlabs.com diff --git a/admin/.gitignore b/admin/.gitignore new file mode 100644 index 0000000..04c01ba --- /dev/null +++ b/admin/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +dist/ \ No newline at end of file diff --git a/admin/index.html b/admin/index.html new file mode 100644 index 0000000..8f95fb8 --- /dev/null +++ b/admin/index.html @@ -0,0 +1,15 @@ + + +
+ + + + + +Admin console
+Sign in to continue
+ +{screen.message}
+{screen.message}
+ +{screen.message}
+ +{screen.message}
+ + ++ {preview.message} +
+ ); + } + let text: string; + if (preview.dates.length === 0) { + text = "No upcoming dates in the next year."; + } else { + const labels = preview.dates.map(occurrenceLabel); + // A full page means the rule keeps going past the last shown date. + if (preview.dates.length >= PREVIEW_COUNT) labels.push("…"); + text = `Next: ${labels.join(" · ")}`; + } + return ( ++ {text} +
+ ); +} + +function Field({ label, id, children }: { label: string; id: string; children: ReactNode }) { + return ( ++ Also assigned (inactive): {inactiveAssignees.map((a) => a.display_name).join(", ")}. + Kept unless you change the selection. +
+ ) : null} ++ {recurrenceSummary({ + rule_type: "monthly_weekday", + interval: parseWhole(draft.interval, 1, 999) ?? 1, + weekday_set: null, + day_of_month: null, + nth_weekday: draft.monthlyWeekday.nth, + nth_weekday_weekday: draft.monthlyWeekday.weekday, + month: null, + start_date: draft.startDate, + end_date: draft.endDate, + })} +
+ ) : null} + {draft.repeats === "yearly" ? ( + + ) : null} + {(draft.repeats === "monthly" && !draft.monthlyWeekday) || draft.repeats === "yearly" ? ( + update({ dayOfMonth: e.target.value })} + /> + ) : null} +Pick a window to set its due time.
+ ) : ( ++ {error} +
+ ) : null} + + {confirming ? ( ++ Saving will unassign{" "} + {inactiveAssignees.map((a) => a.display_name).join(", ")}. Inactive children can't be + assigned again until they are reactivated. +
+{state.message}
+ +{sub}
+Append-only event log · last {RANGE_DAYS} days
+{shown.message}
+ +Beats the custody pattern for these dates
++ {headline} +
+{detail}
++ {error} +
+ ) : null} + +{sub}
+{state.message}
+ ++ {save.message} +
+ ) : save.kind === "saved" ? ( ++ Pattern saved. +
+ ) : null} ++ {deletion.message} +
+ ) : null} + > + ); + } + + return ( +{sub}
+{state.message}
+ +