diff --git a/tsugini/package-lock.json b/tsugini/package-lock.json index d11a6e29..c2b4ed78 100644 --- a/tsugini/package-lock.json +++ b/tsugini/package-lock.json @@ -12,6 +12,8 @@ "@pinia/colada": "^0.16.1", "@pinia/colada-plugin-auto-refetch": "^0.1.0", "@quasar/extras": "^1.16.4", + "@vueuse/core": "^13.3.0", + "marked": "^15.0.12", "openapi-fetch": "^0.14.0", "pinia": "^3.0.1", "pinia-plugin-persistedstate": "^4.3.0", @@ -1928,6 +1930,12 @@ "@types/send": "*" } }, + "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/@typescript-eslint/eslint-plugin": { "version": "8.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.0.tgz", @@ -2461,6 +2469,44 @@ "integrity": "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==", "license": "MIT" }, + "node_modules/@vueuse/core": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.3.0.tgz", + "integrity": "sha512-uYRz5oEfebHCoRhK4moXFM3NSCd5vu2XMLOq/Riz5FdqZMy2RvBtazdtL3gEcmDyqkztDe9ZP/zymObMIbiYSg==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "13.3.0", + "@vueuse/shared": "13.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/metadata": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.3.0.tgz", + "integrity": "sha512-42IzJIOYCKIb0Yjv1JfaKpx8JlCiTmtCWrPxt7Ja6Wzoq0h79+YVXmBV03N966KEmDEESTbp5R/qO3AB5BDnGw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.3.0.tgz", + "integrity": "sha512-L1QKsF0Eg9tiZSFXTgodYnu0Rsa2P0En2LuLrIs/jgrkyiDuJSsPZK+tx+wU0mMsYHUYEjNsuE41uqqkuR8VhA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -5590,6 +5636,18 @@ "url": "https://github.com/sponsors/sxzz" } }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", diff --git a/tsugini/package.json b/tsugini/package.json index 8e9720d5..caff686c 100644 --- a/tsugini/package.json +++ b/tsugini/package.json @@ -19,6 +19,8 @@ "@pinia/colada": "^0.16.1", "@pinia/colada-plugin-auto-refetch": "^0.1.0", "@quasar/extras": "^1.16.4", + "@vueuse/core": "^13.3.0", + "marked": "^15.0.12", "openapi-fetch": "^0.14.0", "pinia": "^3.0.1", "pinia-plugin-persistedstate": "^4.3.0", diff --git a/tsugini/quasar.config.ts b/tsugini/quasar.config.ts index 9281cb2b..b962d70f 100644 --- a/tsugini/quasar.config.ts +++ b/tsugini/quasar.config.ts @@ -3,7 +3,11 @@ import { defineConfig } from '#q-app/wrappers' import { VueRouterAutoImports } from 'unplugin-vue-router' -import { QuasarResolver } from 'unplugin-vue-components/resolvers' +import { + QuasarResolver, + VueUseComponentsResolver, + VueUseDirectiveResolver, +} from 'unplugin-vue-components/resolvers' import { fileURLToPath } from 'node:url' export default defineConfig((ctx) => { @@ -91,11 +95,7 @@ export default defineConfig((ctx) => { dts: 'src/components.d.ts', directoryAsNamespace: true, collapseSamePrefixes: true, - resolvers: [ - QuasarResolver(), - // VueUseComponentsResolver(), - // VueUseDirectiveResolver() - ], + resolvers: [QuasarResolver()], }, ], [ @@ -149,7 +149,7 @@ export default defineConfig((ctx) => { // directives: [], // Quasar plugins - plugins: ['Notify'], + plugins: ['Notify', 'Dialog'], }, // animations: 'all', // --- includes all animations diff --git a/tsugini/src/App.vue b/tsugini/src/App.vue index c1007ccd..418a2fad 100644 --- a/tsugini/src/App.vue +++ b/tsugini/src/App.vue @@ -3,7 +3,14 @@ diff --git a/tsugini/src/components/EmptyState.vue b/tsugini/src/components/KEmptyState.vue similarity index 100% rename from tsugini/src/components/EmptyState.vue rename to tsugini/src/components/KEmptyState.vue diff --git a/tsugini/src/components/ThemeSelector.vue b/tsugini/src/components/ThemeSelector.vue new file mode 100644 index 00000000..3330c48c --- /dev/null +++ b/tsugini/src/components/ThemeSelector.vue @@ -0,0 +1,47 @@ + + + + + + + diff --git a/tsugini/src/components/app/Bar.vue b/tsugini/src/components/app/Bar.vue index 06e231f4..f249acca 100644 --- a/tsugini/src/components/app/Bar.vue +++ b/tsugini/src/components/app/Bar.vue @@ -1,11 +1,16 @@ diff --git a/tsugini/src/components/app/drawer/menu/Server.vue b/tsugini/src/components/app/drawer/menu/Server.vue index 68e47321..8970c3f7 100644 --- a/tsugini/src/components/app/drawer/menu/Server.vue +++ b/tsugini/src/components/app/drawer/menu/Server.vue @@ -33,21 +33,12 @@ }} - - - - - - - - - - {{ @@ -64,6 +55,7 @@ to="/server/announcements" clickable :inset-level="1" + active-class="drawer-menu-active" > {{ $formatMessage({ @@ -176,4 +169,5 @@ const expanded = ref(false) diff --git a/tsugini/src/components/form/README.md b/tsugini/src/components/form/README.md new file mode 100644 index 00000000..d4abb81f --- /dev/null +++ b/tsugini/src/components/form/README.md @@ -0,0 +1 @@ +Simple forms that can be wrapped by a `v-form`, or used within a `DialogEditConfirm`. diff --git a/tsugini/src/components/form/user/ChangePassword.vue b/tsugini/src/components/form/user/ChangePassword.vue new file mode 100644 index 00000000..009151e0 --- /dev/null +++ b/tsugini/src/components/form/user/ChangePassword.vue @@ -0,0 +1,128 @@ + + + diff --git a/tsugini/src/components/form/user/Edit.vue b/tsugini/src/components/form/user/Edit.vue new file mode 100644 index 00000000..836183e9 --- /dev/null +++ b/tsugini/src/components/form/user/Edit.vue @@ -0,0 +1,373 @@ + + + diff --git a/tsugini/src/css/quasar.variables.scss b/tsugini/src/css/quasar.variables.scss index 2605a0df..9f1d50c4 100644 --- a/tsugini/src/css/quasar.variables.scss +++ b/tsugini/src/css/quasar.variables.scss @@ -12,14 +12,26 @@ // to match your app's branding. // Tip: Use the "Theme Builder" on Quasar's documentation website. -$primary: #1976d2; -$secondary: #26a69a; -$accent: #9c27b0; +//$primary: #1976d2; +//$secondary: #26a69a; +//$accent: #9c27b0; +// +//$dark: #1d1d1d; +//$dark-page: #121212; +// +//$positive: #21ba45; +//$negative: #c10015; +//$info: #31ccec; +//$warning: #f2c037; -$dark: #1d1d1d; -$dark-page: #121212; +$primary : #005ed3; +$secondary : #fec000; +$accent : #ff0335; -$positive: #21ba45; -$negative: #c10015; -$info: #31ccec; -$warning: #f2c037; +$dark : #1d1d1d; +$dark-page : #121212; + +$positive : #4caf50; +$negative : #b00020; +$info : #2196f3; +$warning : #fb8c00; diff --git a/tsugini/src/pages/login.vue b/tsugini/src/pages/login.vue index 1113f711..5175d339 100644 --- a/tsugini/src/pages/login.vue +++ b/tsugini/src/pages/login.vue @@ -21,10 +21,7 @@ autofocus :disable="isLoading" lazy-rules - :rules="[ - (x) => !!x || 'Required', - (x, r) => r.email(x) || 'Must be a valid email address', - ]" + :rules="[required(), (x, r) => r.email(x) || 'Must be a valid email address']" /> @@ -37,7 +34,7 @@ type="password" :disable="isLoading" lazy-rules - :rules="[(x) => !!x || 'Required']" + :rules="[required()]" /> @@ -73,6 +70,7 @@ import type { ErrorCause } from 'api/komga-client' import { komgaClient } from 'api/komga-client' import { useAppStore } from 'stores/app' import { Notify } from 'quasar' +import { required } from 'utils/rules' const username = ref('') const password = ref('') diff --git a/tsugini/src/pages/server/announcements.vue b/tsugini/src/pages/server/announcements.vue index 4c214185..67169d5c 100644 --- a/tsugini/src/pages/server/announcements.vue +++ b/tsugini/src/pages/server/announcements.vue @@ -20,7 +20,7 @@ - .announcement h2 { font-size: 24px; + line-height: 1.5rem; } diff --git a/tsugini/src/pages/server/updates.vue b/tsugini/src/pages/server/updates.vue index 1436ae1a..f1a579a5 100644 --- a/tsugini/src/pages/server/updates.vue +++ b/tsugini/src/pages/server/updates.vue @@ -1,141 +1,136 @@ diff --git a/tsugini/src/pages/server/users.vue b/tsugini/src/pages/server/users.vue index 0982845f..59da7710 100644 --- a/tsugini/src/pages/server/users.vue +++ b/tsugini/src/pages/server/users.vue @@ -1,185 +1,223 @@ - + meta: requiresRole: ADMIN diff --git a/tsugini/src/stores/app.ts b/tsugini/src/stores/app.ts index 0ec68ceb..4beff456 100644 --- a/tsugini/src/stores/app.ts +++ b/tsugini/src/stores/app.ts @@ -5,6 +5,7 @@ export const useAppStore = defineStore('app', { state: () => ({ loginRememberMe: false, drawer: !useQuasar().platform.is.mobile.valueOf(), + theme: 'auto' as 'auto' | boolean, }), persist: true, }) diff --git a/tsugini/src/styles/_chip.scss b/tsugini/src/styles/_chip.scss new file mode 100644 index 00000000..92b13095 --- /dev/null +++ b/tsugini/src/styles/_chip.scss @@ -0,0 +1,26 @@ +@use "quasar/src/css/variables" as q; + +.body--light { + .chip-negative { + background: q.$red-1; + color: q.$red-10; + } + .chip-info { + background: q.$blue-1; + color: q.$blue; + } +} +.body--dark { + .q-chip { + background: #3A3A3A; + color: #D8D8D8; + } + .chip-negative { + background: #36292C; + color: #AB5866; + } + .chip-info { + background: #172535; + color: #218CE3; + } +} diff --git a/tsugini/src/styles/_drawer.scss b/tsugini/src/styles/_drawer.scss new file mode 100644 index 00000000..39ec4fae --- /dev/null +++ b/tsugini/src/styles/_drawer.scss @@ -0,0 +1,8 @@ +@use "quasar/src/css/variables" as q; + +.body--light .drawer-menu-active { + background: q.$grey-3; +} +.body--dark .drawer-menu-active { + background: q.$grey-9; +} diff --git a/tsugini/src/styles/_global.scss b/tsugini/src/styles/_global.scss index 6a0eb653..4c904a6d 100644 --- a/tsugini/src/styles/_global.scss +++ b/tsugini/src/styles/_global.scss @@ -1,3 +1,5 @@ +@use './chip'; + .link-none { text-decoration: none; } diff --git a/tsugini/src/utils/rules.ts b/tsugini/src/utils/rules.ts new file mode 100644 index 00000000..b5b88a74 --- /dev/null +++ b/tsugini/src/utils/rules.ts @@ -0,0 +1,7 @@ +export function required(err?: string) { + return (v: unknown) => !!v || err || 'Required' +} + +export function sameAs(other?: string, err?: string) { + return (v: unknown) => other === v || err || 'Field must have the same value' +}