users: admin CRUD API and admin shell UI
ci / check (push) Canceled after 0s
ci / images (push) Canceled after 0s

Login, forced password change, admin layout with every 05 §2 page in the
sidebar (unbuilt ones name their milestone), Users table/cards + drawer
(General tab), one-time temporary passwords, reset password. The last
active admin can't be demoted, deactivated or deleted. Audit diffs only
mask secret strings. scripts/e2e has a dependency-free CDP browser check
for this task's acceptance flow.

Task: M1-T4
This commit is contained in:
iamdoubz committed 2026-10-17 15:18:08 -05:00
1 parent 13733ac262
commit 9a81e37725
25 files changed
+1774 -33

No files matched your search

+131
View File
@@ -75,15 +75,24 @@ importers:
apps/web:
dependencies:
'@hookform/resolvers':
specifier: ^5.9.1
version: 5.9.1(ajv@8.20.0)(react-hook-form@7.90.0(@types/react@19.3.0)(react@19.3.1))(zod@4.6.7)
'@selfphoneng/shared':
specifier: workspace:*
version: link:../../packages/shared
'@tanstack/react-query':
specifier: ^5.104.3
version: 5.104.3(react@19.3.1)
react:
specifier: ^19.3.1
version: 19.3.1
react-dom:
specifier: ^19.3.1
version: 19.3.1(react@19.3.1)
react-hook-form:
specifier: ^7.90.0
version: 7.90.0(@types/react@19.3.0)(react@19.3.1)
react-router:
specifier: ^8.4.0
version: 8.4.0(react-dom@19.3.1(react@19.3.1))(react@19.3.1)
@@ -718,6 +727,84 @@ packages:
'@fastify/static@10.1.6':
resolution: {integrity: sha512-f3x9XisI+y6AIhIfA/+Q3SFsuXeqNNk5HQh5htgHsPxmMLb4pLFskV6eFaLSW3IbI4azrS5np+/tkzXNH99LyQ==}
'@hookform/resolvers@5.9.1':
resolution: {integrity: sha512-7b7vsbraJxKgjVSA1Nur9tLwj539WGJUBLA7QNvXnFoT2pM5Z7G+6rlukk4B2/QrTZy6huRtH6wKeESPKuIr6w==}
peerDependencies:
'@sinclair/typebox': '>=0.25.24'
'@standard-schema/spec': ^1.0.0
'@typeschema/main': '>=0.13.7'
'@vinejs/vine': ^2.0.0 || ^3.0.0 || ^4.0.0
ajv: ^8.12.0
ajv-errors: ^3.0.0
ajv-formats: ^2.1.1
arktype: ^2.0.0
ata-validator: ^1.2.0
class-transformer: '>=0.4.0'
class-validator: '>=0.12.0'
computed-types: ^1.0.0
effect: ^3.10.3
fluentvalidation-ts: ^3.0.0
fp-ts: ^2.7.0
io-ts: ^2.0.0
joi: ^17.0.0 || ^18.0.0
nope-validator: '>=0.12.0'
react-hook-form: ^7.55.0
superstruct: '>=0.12.0'
typanion: ^3.3.2
valibot: '>=0.31.0 || ^1.0.0-beta.4 || ^1.0.0-rc'
vest: '>=6.0.0'
yup: ^1.0.0
zod: ^3.25.0 || ^4.0.0
peerDependenciesMeta:
'@sinclair/typebox':
optional: true
'@standard-schema/spec':
optional: true
'@typeschema/main':
optional: true
'@vinejs/vine':
optional: true
ajv:
optional: true
ajv-errors:
optional: true
ajv-formats:
optional: true
arktype:
optional: true
ata-validator:
optional: true
class-transformer:
optional: true
class-validator:
optional: true
computed-types:
optional: true
effect:
optional: true
fluentvalidation-ts:
optional: true
fp-ts:
optional: true
io-ts:
optional: true
joi:
optional: true
nope-validator:
optional: true
superstruct:
optional: true
typanion:
optional: true
valibot:
optional: true
vest:
optional: true
yup:
optional: true
zod:
optional: true
'@humanfs/core@0.19.2':
resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==}
engines: {node: '>=18.18.0'}
@@ -961,6 +1048,9 @@ packages:
'@rolldown/pluginutils@1.0.1':
resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
'@standard-schema/utils@0.3.0':
resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
'@tailwindcss/node@4.3.3':
resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==}
@@ -1055,6 +1145,14 @@ packages:
peerDependencies:
vite: ^5.2.0 || ^6 || ^7 || ^8
'@tanstack/query-core@5.104.3':
resolution: {integrity: sha512-DZY9TuJAOgU3UyHUhXb4CcTYEHNZszlwkPg4VViIwTxOKzLprPUU+rbaPZQd3srqkGOtpaDdKpW972UV/LKlcg==}
'@tanstack/react-query@5.104.3':
resolution: {integrity: sha512-5Ts1ImwJKqoJgPkLxl9WrPQy1JEoEmti3MaaODCfBamHUGFk57IFrc+AIksPq+BAm10EsroX56qm398Z42LG7w==}
peerDependencies:
react: ^18 || ^19
'@types/chai@5.2.3':
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
@@ -2103,6 +2201,16 @@ packages:
peerDependencies:
react: ^19.3.1
react-hook-form@7.90.0:
resolution: {integrity: sha512-+NdG0FLzCPhFOp2NNxaUYLF9fyZhNjx9FTBBSYWF71/l6BBlmIc/j6xlp3DbaHWLJbIG7Zd8oznA19xJClU97w==}
engines: {node: '>=18.0.0'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17 || ^18 || ^19
peerDependenciesMeta:
'@types/react':
optional: true
react-router@8.4.0:
resolution: {integrity: sha512-JtydAkBvU9UTEe5qNC+POhu+ZBNM7rlKY2IegwXc7Idj7xfRG16x5RZrw3mju+XlqBxfvb2ky9P3jUp9WyWC1g==}
engines: {node: '>=22.22.0'}
@@ -2833,6 +2941,14 @@ snapshots:
fastq: 1.20.3
glob: 13.0.6
'@hookform/resolvers@5.9.1(ajv@8.20.0)(react-hook-form@7.90.0(@types/react@19.3.0)(react@19.3.1))(zod@4.6.7)':
dependencies:
'@standard-schema/utils': 0.3.0
react-hook-form: 7.90.0(@types/react@19.3.0)(react@19.3.1)
optionalDependencies:
ajv: 8.20.0
zod: 4.6.7
'@humanfs/core@0.19.2':
dependencies:
'@humanfs/types': 0.15.0
@@ -2986,6 +3102,8 @@ snapshots:
'@rolldown/pluginutils@1.0.1': {}
'@standard-schema/utils@0.3.0': {}
'@tailwindcss/node@4.3.3':
dependencies:
'@jridgewell/remapping': 2.3.5
@@ -3054,6 +3172,13 @@ snapshots:
tailwindcss: 4.3.3
vite: 8.3.4(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)
'@tanstack/query-core@5.104.3': {}
'@tanstack/react-query@5.104.3(react@19.3.1)':
dependencies:
'@tanstack/query-core': 5.104.3
react: 19.3.1
'@types/chai@5.2.3':
dependencies:
'@types/deep-eql': 4.0.2
@@ -3976,6 +4101,12 @@ snapshots:
react: 19.3.1
scheduler: 0.28.0
react-hook-form@7.90.0(@types/react@19.3.0)(react@19.3.1):
dependencies:
react: 19.3.1
optionalDependencies:
'@types/react': 19.3.0
react-router@8.4.0(react-dom@19.3.1(react@19.3.1))(react@19.3.1):
dependencies:
'@remix-run/route-pattern': 0.22.1