From 59109ef298f3dadb013092708fd1e818e7c4456c Mon Sep 17 00:00:00 2001 From: ajdj100 Date: Thu, 14 Aug 2025 13:25:08 -0400 Subject: [PATCH 01/64] added application form UI --- ui/package-lock.json | 295 +++++++++++++++++- ui/package.json | 7 +- ui/src/App.vue | 22 +- ui/src/assets/base.css | 193 ++++++------ ui/src/components/ui/button/Button.vue | 24 ++ ui/src/components/ui/button/index.js | 34 ++ ui/src/components/ui/checkbox/Checkbox.vue | 46 +++ ui/src/components/ui/checkbox/index.js | 1 + ui/src/components/ui/form/FormControl.vue | 19 ++ ui/src/components/ui/form/FormDescription.vue | 20 ++ ui/src/components/ui/form/FormItem.vue | 19 ++ ui/src/components/ui/form/FormLabel.vue | 25 ++ ui/src/components/ui/form/FormMessage.vue | 22 ++ ui/src/components/ui/form/index.js | 11 + ui/src/components/ui/form/injectionKeys.js | 1 + ui/src/components/ui/form/useFormField.js | 36 +++ ui/src/components/ui/input/Input.vue | 32 ++ ui/src/components/ui/input/index.js | 1 + ui/src/components/ui/label/Label.vue | 29 ++ ui/src/components/ui/label/index.js | 1 + ui/src/components/ui/separator/Separator.vue | 28 ++ ui/src/components/ui/separator/index.js | 1 + ui/src/components/ui/textarea/Textarea.vue | 30 ++ ui/src/components/ui/textarea/index.js | 1 + ui/src/pages/ApplicationForm.vue | 216 +++++++++++++ ui/src/router/index.js | 3 +- 26 files changed, 1010 insertions(+), 107 deletions(-) create mode 100644 ui/src/components/ui/button/Button.vue create mode 100644 ui/src/components/ui/button/index.js create mode 100644 ui/src/components/ui/checkbox/Checkbox.vue create mode 100644 ui/src/components/ui/checkbox/index.js create mode 100644 ui/src/components/ui/form/FormControl.vue create mode 100644 ui/src/components/ui/form/FormDescription.vue create mode 100644 ui/src/components/ui/form/FormItem.vue create mode 100644 ui/src/components/ui/form/FormLabel.vue create mode 100644 ui/src/components/ui/form/FormMessage.vue create mode 100644 ui/src/components/ui/form/index.js create mode 100644 ui/src/components/ui/form/injectionKeys.js create mode 100644 ui/src/components/ui/form/useFormField.js create mode 100644 ui/src/components/ui/input/Input.vue create mode 100644 ui/src/components/ui/input/index.js create mode 100644 ui/src/components/ui/label/Label.vue create mode 100644 ui/src/components/ui/label/index.js create mode 100644 ui/src/components/ui/separator/Separator.vue create mode 100644 ui/src/components/ui/separator/index.js create mode 100644 ui/src/components/ui/textarea/Textarea.vue create mode 100644 ui/src/components/ui/textarea/index.js create mode 100644 ui/src/pages/ApplicationForm.vue diff --git a/ui/package-lock.json b/ui/package-lock.json index 37cea1d..9ba76b0 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -9,15 +9,20 @@ "version": "0.0.0", "dependencies": { "@tailwindcss/vite": "^4.1.11", + "@vee-validate/zod": "^4.15.1", + "@vueuse/core": "^13.6.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-vue-next": "^0.539.0", "pinia": "^3.0.3", + "reka-ui": "^2.4.1", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.11", "tw-animate-css": "^1.3.6", + "vee-validate": "^4.15.1", "vue": "^3.5.18", - "vue-router": "^4.5.1" + "vue-router": "^4.5.1", + "zod": "^3.25.76" }, "devDependencies": { "@types/node": "^24.2.1", @@ -913,6 +918,86 @@ "node": ">=18" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz", + "integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@floating-ui/vue": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@floating-ui/vue/-/vue-1.1.8.tgz", + "integrity": "sha512-SNJAa1jbT8Gh1LvWw2uIIViLL0saV2bCY59ISCvJzhbut5DSb2H3LKUK49Xkd7SixTNHKX4LFu59nbwIXt9jjQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.3", + "@floating-ui/utils": "^0.2.10", + "vue-demi": ">=0.13.0" + } + }, + "node_modules/@floating-ui/vue/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@internationalized/date": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.2.tgz", + "integrity": "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/number": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.4.tgz", + "integrity": "sha512-P+/h+RDaiX8EGt3shB9AYM1+QgkvHmJ5rKi4/59k4sg9g58k9rqsRW0WxRO7jCoHyvVbFRRFKmVTdFYdehrxHg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -1254,6 +1339,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@tailwindcss/node": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", @@ -1516,6 +1610,32 @@ "vite": "^5.2.0 || ^6 || ^7" } }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz", + "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/vue-virtual": { + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.12.tgz", + "integrity": "sha512-vhF7kEU9EXWXh+HdAwKJ2m3xaOnTTmgcdXcF2pim8g4GvI7eRrk2YRuV5nUlZnd/NbCIX4/Ja2OZu5EjJL06Ww==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.13.12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.0.0" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1532,6 +1652,25 @@ "undici-types": "~7.10.0" } }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "license": "MIT" + }, + "node_modules/@vee-validate/zod": { + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@vee-validate/zod/-/zod-4.15.1.tgz", + "integrity": "sha512-329Z4TDBE5Vx0FdbA8S4eR9iGCFFUNGbxjpQ20ff5b5wGueScjocUIx9JHPa79LTG06RnlUR4XogQsjN4tecKA==", + "license": "MIT", + "dependencies": { + "type-fest": "^4.8.3", + "vee-validate": "4.15.1" + }, + "peerDependencies": { + "zod": "^3.24.0" + } + }, "node_modules/@vitejs/plugin-vue": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", @@ -1798,6 +1937,44 @@ "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==", "license": "MIT" }, + "node_modules/@vueuse/core": { + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.6.0.tgz", + "integrity": "sha512-DJbD5fV86muVmBgS9QQPddVX7d9hWYswzlf4bIyUD2dj8GC46R1uNClZhVAmsdVts4xb2jwp1PbpuiA50Qee1A==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "13.6.0", + "@vueuse/shared": "13.6.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/metadata": { + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.6.0.tgz", + "integrity": "sha512-rnIH7JvU7NjrpexTsl2Iwv0V0yAx9cw7+clymjKuLSXG0QMcLD0LDgdNmXic+qL0SGvgSVPEpM9IDO/wqo1vkQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.6.0.tgz", + "integrity": "sha512-pDykCSoS2T3fsQrYqf9SyF0QXWHmcGPQ+qiOVjlYSzlWd9dgppB2bFSM1GgKKkt7uzn0BBMV3IbJsUfHG2+BCg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, "node_modules/ansis": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.1.0.tgz", @@ -1808,6 +1985,18 @@ "node": ">=14" } }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/birpc": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.5.0.tgz", @@ -2021,6 +2210,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", @@ -2781,7 +2976,6 @@ "version": "2.0.11", "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "dev": true, "license": "MIT" }, "node_modules/open": { @@ -2922,6 +3116,63 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/reka-ui": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.4.1.tgz", + "integrity": "sha512-NB7DrCsODN8MH02BWtgiExygfFcuuZ5/PTn6fMgjppmFHqePvNhmSn1LEuF35nel6PFbA4v+gdj0IoGN1yZ+vw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.6.13", + "@floating-ui/vue": "^1.1.6", + "@internationalized/date": "^3.5.0", + "@internationalized/number": "^3.5.0", + "@tanstack/vue-virtual": "^3.12.0", + "@vueuse/core": "^12.5.0", + "@vueuse/shared": "^12.5.0", + "aria-hidden": "^1.2.4", + "defu": "^6.1.4", + "ohash": "^2.0.11" + }, + "peerDependencies": { + "vue": ">= 3.2.0" + } + }, + "node_modules/reka-ui/node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/reka-ui/node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/reka-ui/node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", + "license": "MIT", + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", @@ -3161,6 +3412,12 @@ "node": ">=6" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/tw-animate-css": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.6.tgz", @@ -3170,6 +3427,18 @@ "url": "https://github.com/sponsors/Wombosvideo" } }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/undici-types": { "version": "7.10.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", @@ -3238,6 +3507,19 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/vee-validate": { + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-4.15.1.tgz", + "integrity": "sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.5.2", + "type-fest": "^4.8.3" + }, + "peerDependencies": { + "vue": "^3.4.26" + } + }, "node_modules/vite": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", @@ -3536,6 +3818,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/ui/package.json b/ui/package.json index 6d4d3c6..a6b1f4f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -13,15 +13,20 @@ }, "dependencies": { "@tailwindcss/vite": "^4.1.11", + "@vee-validate/zod": "^4.15.1", + "@vueuse/core": "^13.6.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-vue-next": "^0.539.0", "pinia": "^3.0.3", + "reka-ui": "^2.4.1", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.11", "tw-animate-css": "^1.3.6", + "vee-validate": "^4.15.1", "vue": "^3.5.18", - "vue-router": "^4.5.1" + "vue-router": "^4.5.1", + "zod": "^3.25.76" }, "devDependencies": { "@types/node": "^24.2.1", diff --git a/ui/src/App.vue b/ui/src/App.vue index 6ec9f60..e53ff3a 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -1,11 +1,21 @@ - + diff --git a/ui/src/assets/base.css b/ui/src/assets/base.css index 1a59341..4ce345d 100644 --- a/ui/src/assets/base.css +++ b/ui/src/assets/base.css @@ -1,102 +1,103 @@ @import "tailwindcss"; -@import "tw-animate-css"; + + +@custom-variant dark (&:is(.dark *)); :root { - --background: oklch(0.9911 0 0); - --foreground: oklch(0.2046 0 0); - --card: oklch(0.9911 0 0); - --card-foreground: oklch(0.2046 0 0); - --popover: oklch(0.9911 0 0); - --popover-foreground: oklch(0.4386 0 0); - --primary: oklch(1.0000 0 0); - --primary-foreground: oklch(0.3709 0.0313 95.1202); - --secondary: oklch(0.9940 0 0); - --secondary-foreground: oklch(0.2046 0 0); - --muted: oklch(0.9461 0 0); - --muted-foreground: oklch(0.2435 0 0); - --accent: oklch(0.9461 0 0); - --accent-foreground: oklch(0.2435 0 0); - --destructive: oklch(0.6704 0.2070 300.0793); + --background: oklch(0.2046 0 0); + --foreground: oklch(0.9219 0 0); + --card: oklch(0.2686 0 0); + --card-foreground: oklch(0.9219 0 0); + --popover: oklch(0.2686 0 0); + --popover-foreground: oklch(0.9219 0 0); + --primary: oklch(0.7686 0.1647 70.0804); + --primary-foreground: oklch(0 0 0); + --secondary: oklch(0.2686 0 0); + --secondary-foreground: oklch(0.9219 0 0); + --muted: oklch(0.2686 0 0); + --muted-foreground: oklch(0.7155 0 0); + --accent: oklch(0.4732 0.1247 46.2007); + --accent-foreground: oklch(0.9243 0.1151 95.7459); + --destructive: oklch(0.6368 0.2078 25.3313); --destructive-foreground: oklch(1.0000 0 0); - --border: oklch(0.9037 0 0); - --input: oklch(0.9731 0 0); - --ring: oklch(1.0000 0 0); - --chart-1: oklch(1.0000 0 0); - --chart-2: oklch(0.9936 0.0111 141.2643); - --chart-3: oklch(1.0000 0 0); - --chart-4: oklch(0.8317 0.1444 322.3270); - --chart-5: oklch(0.9397 0.1746 103.9958); - --sidebar: oklch(0.9911 0 0); - --sidebar-foreground: oklch(0.5452 0 0); - --sidebar-primary: oklch(1.0000 0 0); - --sidebar-primary-foreground: oklch(0.3709 0.0313 95.1202); - --sidebar-accent: oklch(0.9461 0 0); - --sidebar-accent-foreground: oklch(0.2435 0 0); - --sidebar-border: oklch(0.9037 0 0); - --sidebar-ring: oklch(1.0000 0 0); - --font-sans: Outfit, sans-serif; - --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; - --font-mono: monospace; - --radius: 0.5rem; - --shadow-2xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.09); - --shadow-xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.09); - --shadow-sm: 0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 1px 2px -1px hsl(0 0% 0% / 0.17); - --shadow: 0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 1px 2px -1px hsl(0 0% 0% / 0.17); - --shadow-md: 0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 2px 4px -1px hsl(0 0% 0% / 0.17); - --shadow-lg: 0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 4px 6px -1px hsl(0 0% 0% / 0.17); - --shadow-xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 8px 10px -1px hsl(0 0% 0% / 0.17); - --shadow-2xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.43); - --tracking-normal: 0.025em; - --spacing: 0.25rem; + --border: oklch(0.3715 0 0); + --input: oklch(0.3715 0 0); + --ring: oklch(0.7686 0.1647 70.0804); + --chart-1: oklch(0.8369 0.1644 84.4286); + --chart-2: oklch(0.6658 0.1574 58.3183); + --chart-3: oklch(0.4732 0.1247 46.2007); + --chart-4: oklch(0.5553 0.1455 48.9975); + --chart-5: oklch(0.4732 0.1247 46.2007); + --sidebar: oklch(0.1684 0 0); + --sidebar-foreground: oklch(0.9219 0 0); + --sidebar-primary: oklch(0.7686 0.1647 70.0804); + --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-accent: oklch(0.4732 0.1247 46.2007); + --sidebar-accent-foreground: oklch(0.9243 0.1151 95.7459); + --sidebar-border: oklch(0.3715 0 0); + --sidebar-ring: oklch(0.7686 0.1647 70.0804); + --font-sans: Inter, sans-serif; + --font-serif: Source Serif 4, serif; + --font-mono: JetBrains Mono, monospace; + --radius: 0.375rem; + --shadow-2xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.07); + --shadow-xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.07); + --shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.14), 0px 1px 2px -2px hsl(0 0% 0% / 0.14); + --shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.14), 0px 1px 2px -2px hsl(0 0% 0% / 0.14); + --shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.14), 0px 2px 4px -2px hsl(0 0% 0% / 0.14); + --shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.14), 0px 4px 6px -2px hsl(0 0% 0% / 0.14); + --shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.14), 0px 8px 10px -2px hsl(0 0% 0% / 0.14); + --shadow-2xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.35); } .dark { - --background: oklch(0.1822 0 0); - --foreground: oklch(1.0000 0 0); - --card: oklch(0.2046 0 0); - --card-foreground: oklch(1.0000 0 0); - --popover: oklch(0.2603 0 0); - --popover-foreground: oklch(0.7348 0 0); - --primary: oklch(0.6277 0.1287 94.1115); - --primary-foreground: oklch(1.0000 0 0); - --secondary: oklch(0.2603 0 0); - --secondary-foreground: oklch(0.9851 0 0); - --muted: oklch(0.2393 0 0); - --muted-foreground: oklch(0.7122 0 0); - --accent: oklch(0.3132 0 0); - --accent-foreground: oklch(0.9851 0 0); - --destructive: oklch(0.3712 0.2143 284.7713); + --background: oklch(0.2046 0 0); + --foreground: oklch(0.9219 0 0); + --card: oklch(0.2686 0 0); + --card-foreground: oklch(0.9219 0 0); + --popover: oklch(0.2686 0 0); + --popover-foreground: oklch(0.9219 0 0); + --primary: oklch(0.7686 0.1647 70.0804); + --primary-foreground: oklch(0 0 0); + --secondary: oklch(0.2686 0 0); + --secondary-foreground: oklch(0.9219 0 0); + --muted: oklch(0.2686 0 0); + --muted-foreground: oklch(0.7155 0 0); + --accent: oklch(0.4732 0.1247 46.2007); + --accent-foreground: oklch(0.9243 0.1151 95.7459); + --destructive: oklch(0.6368 0.2078 25.3313); --destructive-foreground: oklch(1.0000 0 0); - --border: oklch(0.2809 0 0); - --input: oklch(0.2603 0 0); - --ring: oklch(0.9786 0.0203 81.7829); - --chart-1: oklch(0.9786 0.0203 81.7829); - --chart-2: oklch(1.0000 0 0); - --chart-3: oklch(1.0000 0 0); - --chart-4: oklch(0.9312 0.0608 325.1964); - --chart-5: oklch(0.9724 0.1085 114.3821); - --sidebar: oklch(0.1822 0 0); - --sidebar-foreground: oklch(0.6301 0 0); - --sidebar-primary: oklch(0.6277 0.1287 94.1115); + --border: oklch(0.3715 0 0); + --input: oklch(0.3715 0 0); + --ring: oklch(0.7686 0.1647 70.0804); + --chart-1: oklch(0.8369 0.1644 84.4286); + --chart-2: oklch(0.6658 0.1574 58.3183); + --chart-3: oklch(0.4732 0.1247 46.2007); + --chart-4: oklch(0.5553 0.1455 48.9975); + --chart-5: oklch(0.4732 0.1247 46.2007); + --sidebar: oklch(0.1684 0 0); + --sidebar-foreground: oklch(0.9219 0 0); + --sidebar-primary: oklch(0.7686 0.1647 70.0804); --sidebar-primary-foreground: oklch(1.0000 0 0); - --sidebar-accent: oklch(0.3132 0 0); - --sidebar-accent-foreground: oklch(0.9851 0 0); - --sidebar-border: oklch(0.2809 0 0); - --sidebar-ring: oklch(0.9786 0.0203 81.7829); - --font-sans: Outfit, sans-serif; - --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; - --font-mono: monospace; - --radius: 0.5rem; - --shadow-2xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.09); - --shadow-xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.09); - --shadow-sm: 0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 1px 2px -1px hsl(0 0% 0% / 0.17); - --shadow: 0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 1px 2px -1px hsl(0 0% 0% / 0.17); - --shadow-md: 0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 2px 4px -1px hsl(0 0% 0% / 0.17); - --shadow-lg: 0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 4px 6px -1px hsl(0 0% 0% / 0.17); - --shadow-xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 8px 10px -1px hsl(0 0% 0% / 0.17); - --shadow-2xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.43); + --sidebar-accent: oklch(0.4732 0.1247 46.2007); + --sidebar-accent-foreground: oklch(0.9243 0.1151 95.7459); + --sidebar-border: oklch(0.3715 0 0); + --sidebar-ring: oklch(0.7686 0.1647 70.0804); + --font-sans: Inter, sans-serif; + --font-serif: Source Serif 4, serif; + --font-mono: JetBrains Mono, monospace; + --radius: 0.375rem; + --shadow-2xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.07); + --shadow-xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.07); + --shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.14), 0px 1px 2px -2px hsl(0 0% 0% / 0.14); + --shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.14), 0px 1px 2px -2px hsl(0 0% 0% / 0.14); + --shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.14), 0px 2px 4px -2px hsl(0 0% 0% / 0.14); + --shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.14), 0px 4px 6px -2px hsl(0 0% 0% / 0.14); + --shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.14), 0px 8px 10px -2px hsl(0 0% 0% / 0.14); + --shadow-2xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.35); } + @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); @@ -148,15 +149,13 @@ --shadow-lg: var(--shadow-lg); --shadow-xl: var(--shadow-xl); --shadow-2xl: var(--shadow-2xl); - - --tracking-tighter: calc(var(--tracking-normal) - 0.05em); - --tracking-tight: calc(var(--tracking-normal) - 0.025em); - --tracking-normal: var(--tracking-normal); - --tracking-wide: calc(var(--tracking-normal) + 0.025em); - --tracking-wider: calc(var(--tracking-normal) + 0.05em); - --tracking-widest: calc(var(--tracking-normal) + 0.1em); } -body { - letter-spacing: var(--tracking-normal); +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } } \ No newline at end of file diff --git a/ui/src/components/ui/button/Button.vue b/ui/src/components/ui/button/Button.vue new file mode 100644 index 0000000..713a6b9 --- /dev/null +++ b/ui/src/components/ui/button/Button.vue @@ -0,0 +1,24 @@ + + + diff --git a/ui/src/components/ui/button/index.js b/ui/src/components/ui/button/index.js new file mode 100644 index 0000000..83eb0ed --- /dev/null +++ b/ui/src/components/ui/button/index.js @@ -0,0 +1,34 @@ +import { cva } from "class-variance-authority"; + +export { default as Button } from "./Button.vue"; + +export const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", + destructive: + "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); diff --git a/ui/src/components/ui/checkbox/Checkbox.vue b/ui/src/components/ui/checkbox/Checkbox.vue new file mode 100644 index 0000000..84e4a0e --- /dev/null +++ b/ui/src/components/ui/checkbox/Checkbox.vue @@ -0,0 +1,46 @@ + + + diff --git a/ui/src/components/ui/checkbox/index.js b/ui/src/components/ui/checkbox/index.js new file mode 100644 index 0000000..75be342 --- /dev/null +++ b/ui/src/components/ui/checkbox/index.js @@ -0,0 +1 @@ +export { default as Checkbox } from "./Checkbox.vue"; diff --git a/ui/src/components/ui/form/FormControl.vue b/ui/src/components/ui/form/FormControl.vue new file mode 100644 index 0000000..3f624e3 --- /dev/null +++ b/ui/src/components/ui/form/FormControl.vue @@ -0,0 +1,19 @@ + + + diff --git a/ui/src/components/ui/form/FormDescription.vue b/ui/src/components/ui/form/FormDescription.vue new file mode 100644 index 0000000..504a46e --- /dev/null +++ b/ui/src/components/ui/form/FormDescription.vue @@ -0,0 +1,20 @@ + + + diff --git a/ui/src/components/ui/form/FormItem.vue b/ui/src/components/ui/form/FormItem.vue new file mode 100644 index 0000000..d2a45a7 --- /dev/null +++ b/ui/src/components/ui/form/FormItem.vue @@ -0,0 +1,19 @@ + + + diff --git a/ui/src/components/ui/form/FormLabel.vue b/ui/src/components/ui/form/FormLabel.vue new file mode 100644 index 0000000..91f8242 --- /dev/null +++ b/ui/src/components/ui/form/FormLabel.vue @@ -0,0 +1,25 @@ + + + diff --git a/ui/src/components/ui/form/FormMessage.vue b/ui/src/components/ui/form/FormMessage.vue new file mode 100644 index 0000000..15d7f6c --- /dev/null +++ b/ui/src/components/ui/form/FormMessage.vue @@ -0,0 +1,22 @@ + + + diff --git a/ui/src/components/ui/form/index.js b/ui/src/components/ui/form/index.js new file mode 100644 index 0000000..7749894 --- /dev/null +++ b/ui/src/components/ui/form/index.js @@ -0,0 +1,11 @@ +export { default as FormControl } from "./FormControl.vue"; +export { default as FormDescription } from "./FormDescription.vue"; +export { default as FormItem } from "./FormItem.vue"; +export { default as FormLabel } from "./FormLabel.vue"; +export { default as FormMessage } from "./FormMessage.vue"; +export { FORM_ITEM_INJECTION_KEY } from "./injectionKeys"; +export { + Form, + Field as FormField, + FieldArray as FormFieldArray, +} from "vee-validate"; diff --git a/ui/src/components/ui/form/injectionKeys.js b/ui/src/components/ui/form/injectionKeys.js new file mode 100644 index 0000000..533eabd --- /dev/null +++ b/ui/src/components/ui/form/injectionKeys.js @@ -0,0 +1 @@ +export const FORM_ITEM_INJECTION_KEY = Symbol(); diff --git a/ui/src/components/ui/form/useFormField.js b/ui/src/components/ui/form/useFormField.js new file mode 100644 index 0000000..f17b0b4 --- /dev/null +++ b/ui/src/components/ui/form/useFormField.js @@ -0,0 +1,36 @@ +import { + FieldContextKey, + useFieldError, + useIsFieldDirty, + useIsFieldTouched, + useIsFieldValid, +} from "vee-validate"; +import { inject } from "vue"; +import { FORM_ITEM_INJECTION_KEY } from "./injectionKeys"; + +export function useFormField() { + const fieldContext = inject(FieldContextKey); + const fieldItemContext = inject(FORM_ITEM_INJECTION_KEY); + + if (!fieldContext) + throw new Error("useFormField should be used within "); + + const { name } = fieldContext; + const id = fieldItemContext; + + const fieldState = { + valid: useIsFieldValid(name), + isDirty: useIsFieldDirty(name), + isTouched: useIsFieldTouched(name), + error: useFieldError(name), + }; + + return { + id, + name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +} diff --git a/ui/src/components/ui/input/Input.vue b/ui/src/components/ui/input/Input.vue new file mode 100644 index 0000000..dc16a12 --- /dev/null +++ b/ui/src/components/ui/input/Input.vue @@ -0,0 +1,32 @@ + + + diff --git a/ui/src/components/ui/input/index.js b/ui/src/components/ui/input/index.js new file mode 100644 index 0000000..110f046 --- /dev/null +++ b/ui/src/components/ui/input/index.js @@ -0,0 +1 @@ +export { default as Input } from "./Input.vue"; diff --git a/ui/src/components/ui/label/Label.vue b/ui/src/components/ui/label/Label.vue new file mode 100644 index 0000000..03b1932 --- /dev/null +++ b/ui/src/components/ui/label/Label.vue @@ -0,0 +1,29 @@ + + + diff --git a/ui/src/components/ui/label/index.js b/ui/src/components/ui/label/index.js new file mode 100644 index 0000000..38eaa35 --- /dev/null +++ b/ui/src/components/ui/label/index.js @@ -0,0 +1 @@ +export { default as Label } from "./Label.vue"; diff --git a/ui/src/components/ui/separator/Separator.vue b/ui/src/components/ui/separator/Separator.vue new file mode 100644 index 0000000..fc2fb37 --- /dev/null +++ b/ui/src/components/ui/separator/Separator.vue @@ -0,0 +1,28 @@ + + + diff --git a/ui/src/components/ui/separator/index.js b/ui/src/components/ui/separator/index.js new file mode 100644 index 0000000..aae7f1a --- /dev/null +++ b/ui/src/components/ui/separator/index.js @@ -0,0 +1 @@ +export { default as Separator } from "./Separator.vue"; diff --git a/ui/src/components/ui/textarea/Textarea.vue b/ui/src/components/ui/textarea/Textarea.vue new file mode 100644 index 0000000..415a4d4 --- /dev/null +++ b/ui/src/components/ui/textarea/Textarea.vue @@ -0,0 +1,30 @@ + + +