create-nuxt-base 0.3.16 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +4 -2
- package/.oxfmtrc.jsonc +7 -0
- package/CHANGELOG.md +22 -8
- package/nuxt-base-template/.dockerignore +44 -0
- package/nuxt-base-template/.env.example +1 -1
- package/nuxt-base-template/.nuxtrc +1 -0
- package/nuxt-base-template/.oxfmtrc.jsonc +8 -0
- package/nuxt-base-template/Dockerfile.dev +23 -0
- package/nuxt-base-template/README.md +127 -13
- package/nuxt-base-template/app/app.config.ts +67 -0
- package/nuxt-base-template/app/app.vue +10 -2
- package/nuxt-base-template/app/assets/css/tailwind.css +124 -84
- package/nuxt-base-template/app/components/Modal/ModalBackupCodes.vue +117 -0
- package/nuxt-base-template/app/components/Modal/ModalBase.vue +65 -0
- package/nuxt-base-template/app/components/Transition/TransitionSlide.vue +0 -2
- package/nuxt-base-template/app/components/Transition/TransitionSlideBottom.vue +0 -2
- package/nuxt-base-template/app/components/Transition/TransitionSlideRevert.vue +0 -2
- package/nuxt-base-template/app/components/Upload/TusFileUpload.vue +302 -0
- package/nuxt-base-template/app/composables/use-better-auth.ts +25 -0
- package/nuxt-base-template/app/composables/use-file.ts +57 -6
- package/nuxt-base-template/app/composables/use-share.ts +26 -10
- package/nuxt-base-template/app/composables/use-tus-upload.ts +278 -0
- package/nuxt-base-template/app/error.vue +7 -43
- package/nuxt-base-template/app/interfaces/upload.interface.ts +58 -0
- package/nuxt-base-template/app/interfaces/user.interface.ts +12 -0
- package/nuxt-base-template/app/layouts/default.vue +76 -4
- package/nuxt-base-template/app/layouts/slim.vue +5 -0
- package/nuxt-base-template/app/lib/auth-client.ts +135 -0
- package/nuxt-base-template/app/middleware/admin.global.ts +20 -6
- package/nuxt-base-template/app/middleware/auth.global.ts +15 -6
- package/nuxt-base-template/app/middleware/guest.global.ts +18 -0
- package/nuxt-base-template/app/pages/app/settings/security.vue +409 -0
- package/nuxt-base-template/app/pages/auth/2fa.vue +120 -0
- package/nuxt-base-template/app/pages/auth/forgot-password.vue +115 -0
- package/nuxt-base-template/app/pages/auth/login.vue +135 -0
- package/nuxt-base-template/app/pages/auth/register.vue +184 -0
- package/nuxt-base-template/app/pages/auth/reset-password.vue +153 -0
- package/nuxt-base-template/app/pages/index.vue +139 -2
- package/nuxt-base-template/app/utils/crypto.ts +13 -0
- package/nuxt-base-template/docker-entrypoint.sh +21 -0
- package/nuxt-base-template/docs/nuxt.config.ts +4 -0
- package/nuxt-base-template/docs/pages/docs.vue +663 -0
- package/nuxt-base-template/nuxt.config.ts +75 -30
- package/nuxt-base-template/openapi-ts.config.ts +18 -0
- package/nuxt-base-template/oxlint.json +14 -0
- package/nuxt-base-template/package-lock.json +11414 -15883
- package/nuxt-base-template/package.json +48 -50
- package/nuxt-base-template/tests/iam.spec.ts +247 -0
- package/nuxt-base-template/tsconfig.json +1 -1
- package/package.json +15 -12
- package/.eslintignore +0 -14
- package/.eslintrc +0 -3
- package/.prettierignore +0 -5
- package/.prettierrc +0 -6
- package/nuxt-base-template/app/composables/use-context-menu.ts +0 -19
- package/nuxt-base-template/app/composables/use-form-helper.ts +0 -41
- package/nuxt-base-template/app/composables/use-modal.ts +0 -84
- package/nuxt-base-template/app/composables/use-notification.ts +0 -29
- package/nuxt-base-template/app/middleware/logged-in.global.ts +0 -9
- package/nuxt-base-template/app/plugins/auth.server.ts +0 -72
- package/nuxt-base-template/app/plugins/form.plugin.ts +0 -21
- package/nuxt-base-template/app/plugins/pwa.plugin.ts +0 -114
- package/nuxt-base-template/eslint.config.mjs +0 -3
- package/nuxt-base-template/tailwind.config.js +0 -21
|
@@ -2,77 +2,75 @@
|
|
|
2
2
|
"name": "nuxt-base-template",
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"import": {
|
|
8
|
+
"node": "./tslib.es6.mjs"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
8
11
|
},
|
|
9
12
|
"scripts": {
|
|
10
13
|
"init": "npm install",
|
|
11
14
|
"reinit": "npx rimraf package-lock.json && npx nuxt cleanup && npx rimraf node_modules && npm cache clean --force && npm i",
|
|
15
|
+
"clean": "npx rimraf .nuxt .output",
|
|
16
|
+
"dev": "nuxt dev",
|
|
12
17
|
"build": "nuxt build",
|
|
13
|
-
"
|
|
14
|
-
"build:
|
|
15
|
-
"build:
|
|
18
|
+
"build:develop": "npx cross-env NODE_ENV=development nuxt build",
|
|
19
|
+
"build:test": "npx cross-env NODE_ENV=test nuxt build",
|
|
20
|
+
"build:prod": "npx cross-env NODE_ENV=production nuxt build",
|
|
16
21
|
"start": "nuxt dev",
|
|
17
22
|
"start:develop": "node .output/server/index.mjs",
|
|
18
23
|
"start:test": "node .output/server/index.mjs",
|
|
19
24
|
"start:prod": "node .output/server/index.mjs",
|
|
20
25
|
"start:tunnel": "nuxt dev --tunnel",
|
|
21
26
|
"start:extern": "npx cross-env HOST=0.0.0.0 nuxt dev",
|
|
22
|
-
"generate-types": "npx rimraf app/base && npx cross-env GENERATE_TYPES=1 nuxt dev",
|
|
23
|
-
"dev": "nuxt dev",
|
|
24
27
|
"generate": "nuxt generate",
|
|
28
|
+
"generate-types": "openapi-ts",
|
|
25
29
|
"preview": "nuxt preview",
|
|
26
30
|
"postinstall": "nuxt prepare",
|
|
27
|
-
"
|
|
28
|
-
"test": "
|
|
29
|
-
"lint": "
|
|
30
|
-
"lint:fix": "
|
|
31
|
+
"test": "npm run test:e2e",
|
|
32
|
+
"test:e2e": "playwright test",
|
|
33
|
+
"lint": "oxlint app/",
|
|
34
|
+
"lint:fix": "oxlint --fix app/",
|
|
35
|
+
"format": "oxfmt",
|
|
36
|
+
"format:check": "oxfmt --check",
|
|
37
|
+
"check": "npm run lint && npm run format:check",
|
|
38
|
+
"fix": "npm run lint:fix && npm run format"
|
|
31
39
|
},
|
|
32
40
|
"dependencies": {
|
|
33
|
-
"@
|
|
34
|
-
"@
|
|
41
|
+
"@better-auth/passkey": "1.4.10",
|
|
42
|
+
"@hey-api/client-fetch": "0.13.1",
|
|
35
43
|
"@lenne.tech/bug.lt": "latest",
|
|
36
|
-
"@
|
|
37
|
-
"@nuxt/
|
|
38
|
-
"@
|
|
39
|
-
"@vueuse/
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"rimraf": "6.0.1",
|
|
44
|
-
"tailwind-merge": "3.3.1",
|
|
45
|
-
"tailwindcss": "4.1.13",
|
|
46
|
-
"vee-validate": "4.15.1"
|
|
44
|
+
"@nuxt/image": "2.0.0",
|
|
45
|
+
"@nuxt/ui": "4.3.0",
|
|
46
|
+
"@pinia/nuxt": "0.11.3",
|
|
47
|
+
"@vueuse/nuxt": "14.1.0",
|
|
48
|
+
"better-auth": "1.4.10",
|
|
49
|
+
"tus-js-client": "4.3.1",
|
|
50
|
+
"valibot": "1.2.0"
|
|
47
51
|
},
|
|
48
52
|
"devDependencies": {
|
|
49
|
-
"@
|
|
50
|
-
"@nuxt/devtools": "
|
|
51
|
-
"@nuxt/test-utils": "3.
|
|
52
|
-
"@nuxtjs/color-mode": "
|
|
53
|
-
"@nuxtjs/google-fonts": "3.2.0",
|
|
53
|
+
"@hey-api/openapi-ts": "0.90.3",
|
|
54
|
+
"@nuxt/devtools": "3.1.1",
|
|
55
|
+
"@nuxt/test-utils": "3.23.0",
|
|
56
|
+
"@nuxtjs/color-mode": "4.0.0",
|
|
54
57
|
"@nuxtjs/plausible": "2.0.1",
|
|
55
|
-
"@nuxtjs/seo": "3.
|
|
56
|
-
"@
|
|
57
|
-
"@
|
|
58
|
-
"@tailwindcss/
|
|
59
|
-
"@
|
|
60
|
-
"@tailwindcss/vite": "4.1.13",
|
|
61
|
-
"@types/node": "24.3.1",
|
|
62
|
-
"@vitejs/plugin-vue": "6.0.1",
|
|
63
|
-
"@vue/test-utils": "2.4.6",
|
|
58
|
+
"@nuxtjs/seo": "3.3.0",
|
|
59
|
+
"@playwright/test": "1.57.0",
|
|
60
|
+
"@tailwindcss/typography": "0.5.19",
|
|
61
|
+
"@tailwindcss/vite": "4.1.18",
|
|
62
|
+
"@types/node": "25.0.6",
|
|
64
63
|
"dayjs-nuxt": "2.1.11",
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
64
|
+
"jsdom": "27.4.0",
|
|
65
|
+
"nuxt": "4.2.2",
|
|
66
|
+
"oxfmt": "latest",
|
|
67
|
+
"oxlint": "latest",
|
|
68
|
+
"rimraf": "6.1.2",
|
|
69
|
+
"tailwindcss": "4.1.18",
|
|
70
|
+
"typescript": "5.9.3"
|
|
70
71
|
},
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
|
|
74
|
-
"node": "./tslib.es6.mjs"
|
|
75
|
-
}
|
|
76
|
-
}
|
|
72
|
+
"engines": {
|
|
73
|
+
"node": ">=22",
|
|
74
|
+
"npm": ">=10"
|
|
77
75
|
}
|
|
78
76
|
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { expect, test } from '@nuxt/test-utils/playwright';
|
|
2
|
+
|
|
3
|
+
import { authClient, type AuthResponse } from '../app/lib/auth-client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* IAM E2E Tests using Better-Auth Client
|
|
7
|
+
*
|
|
8
|
+
* These tests use the Better-Auth client library directly to communicate
|
|
9
|
+
* with the /iam API endpoints on the running API server.
|
|
10
|
+
*
|
|
11
|
+
* Test flow: Test Runner → Better-Auth Client → API Server (/iam endpoints)
|
|
12
|
+
*
|
|
13
|
+
* Prerequisites:
|
|
14
|
+
* - API server running on http://localhost:3000
|
|
15
|
+
*
|
|
16
|
+
* NOTE: These tests are for DEMO purposes only. If the API server is not
|
|
17
|
+
* running, tests will be skipped with a warning (not treated as failure).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// Flag to track API availability - checked once in beforeAll
|
|
21
|
+
let apiAvailable = false;
|
|
22
|
+
|
|
23
|
+
test.describe.serial('IAM Better-Auth Client Tests', () => {
|
|
24
|
+
// Shared test user credentials - created once in the first test
|
|
25
|
+
const testId = Date.now();
|
|
26
|
+
const testEmail = `playwright-${testId}@iam-test.com`;
|
|
27
|
+
const testPassword = `TestPass${testId}!`;
|
|
28
|
+
const testName = 'Playwright Test User';
|
|
29
|
+
|
|
30
|
+
test.beforeAll(async ({ request }) => {
|
|
31
|
+
// Check if the API server is running
|
|
32
|
+
try {
|
|
33
|
+
const response = await request.get('http://localhost:3000/');
|
|
34
|
+
if (response.ok()) {
|
|
35
|
+
apiAvailable = true;
|
|
36
|
+
console.info('✓ API Server is running at http://localhost:3000');
|
|
37
|
+
}
|
|
38
|
+
} catch (_error) {
|
|
39
|
+
// API is not available - will be handled below
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!apiAvailable) {
|
|
43
|
+
console.warn('');
|
|
44
|
+
console.warn('⚠️ ═══════════════════════════════════════════════════════════');
|
|
45
|
+
console.warn('⚠️ WARNING: API Server is not running!');
|
|
46
|
+
console.warn('⚠️');
|
|
47
|
+
console.warn('⚠️ IAM tests will be SKIPPED (demo tests only)');
|
|
48
|
+
console.warn('⚠️');
|
|
49
|
+
console.warn('⚠️ To run these tests, start the API server first:');
|
|
50
|
+
console.warn('⚠️ cd projects/api && npm start');
|
|
51
|
+
console.warn('⚠️');
|
|
52
|
+
console.warn('⚠️ Then run the tests:');
|
|
53
|
+
console.warn('⚠️ cd projects/app && npm run test:e2e');
|
|
54
|
+
console.warn('⚠️ ═══════════════════════════════════════════════════════════');
|
|
55
|
+
console.warn('');
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// =========================================================================
|
|
60
|
+
// Sign-Up Tests
|
|
61
|
+
// =========================================================================
|
|
62
|
+
|
|
63
|
+
test('1. should sign up a new user via Better-Auth client', async () => {
|
|
64
|
+
test.skip(!apiAvailable, 'API server not running - skipping demo test');
|
|
65
|
+
|
|
66
|
+
const response = (await authClient.signUp.email({
|
|
67
|
+
email: testEmail,
|
|
68
|
+
name: testName,
|
|
69
|
+
password: testPassword,
|
|
70
|
+
})) as AuthResponse;
|
|
71
|
+
|
|
72
|
+
expect(response.error).toBeNull();
|
|
73
|
+
expect(response.data?.user?.email).toBe(testEmail);
|
|
74
|
+
console.info(` ✓ User registered: ${testEmail}`);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('2. should fail sign-up with existing email', async () => {
|
|
78
|
+
test.skip(!apiAvailable, 'API server not running - skipping demo test');
|
|
79
|
+
|
|
80
|
+
const response = (await authClient.signUp.email({
|
|
81
|
+
email: testEmail,
|
|
82
|
+
name: testName,
|
|
83
|
+
password: testPassword,
|
|
84
|
+
})) as AuthResponse;
|
|
85
|
+
|
|
86
|
+
// Better-Auth returns error in response for duplicate email
|
|
87
|
+
expect(response.error).not.toBeNull();
|
|
88
|
+
console.info(' ✓ Duplicate registration correctly rejected');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// =========================================================================
|
|
92
|
+
// Sign-In Tests
|
|
93
|
+
// =========================================================================
|
|
94
|
+
|
|
95
|
+
test('3. should sign in with valid credentials', async () => {
|
|
96
|
+
test.skip(!apiAvailable, 'API server not running - skipping demo test');
|
|
97
|
+
|
|
98
|
+
const response = (await authClient.signIn.email({
|
|
99
|
+
email: testEmail,
|
|
100
|
+
password: testPassword,
|
|
101
|
+
})) as AuthResponse;
|
|
102
|
+
|
|
103
|
+
expect(response.error).toBeNull();
|
|
104
|
+
expect(response.data?.user?.email).toBe(testEmail);
|
|
105
|
+
expect(response.data?.user).toBeDefined();
|
|
106
|
+
expect(response.data?.token).toBeDefined(); // JWT token - presence indicates success
|
|
107
|
+
console.info(` ✓ User signed in: ${testEmail}`);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('4. should fail sign-in with wrong password', async () => {
|
|
111
|
+
test.skip(!apiAvailable, 'API server not running - skipping demo test');
|
|
112
|
+
|
|
113
|
+
const response = (await authClient.signIn.email({
|
|
114
|
+
email: testEmail,
|
|
115
|
+
password: 'WrongPassword123!',
|
|
116
|
+
})) as AuthResponse;
|
|
117
|
+
|
|
118
|
+
expect(response.error).not.toBeNull();
|
|
119
|
+
console.info(' ✓ Wrong password correctly rejected');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('5. should fail sign-in with non-existent email', async () => {
|
|
123
|
+
test.skip(!apiAvailable, 'API server not running - skipping demo test');
|
|
124
|
+
|
|
125
|
+
const response = (await authClient.signIn.email({
|
|
126
|
+
email: 'nonexistent@iam-test.com',
|
|
127
|
+
password: 'SomePassword123!',
|
|
128
|
+
})) as AuthResponse;
|
|
129
|
+
|
|
130
|
+
expect(response.error).not.toBeNull();
|
|
131
|
+
console.info(' ✓ Non-existent user correctly rejected');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// =========================================================================
|
|
135
|
+
// Session Tests
|
|
136
|
+
// =========================================================================
|
|
137
|
+
|
|
138
|
+
test('6. should get session after sign-in', async () => {
|
|
139
|
+
test.skip(!apiAvailable, 'API server not running - skipping demo test');
|
|
140
|
+
|
|
141
|
+
// First sign in
|
|
142
|
+
const signInResponse = (await authClient.signIn.email({
|
|
143
|
+
email: testEmail,
|
|
144
|
+
password: testPassword,
|
|
145
|
+
})) as AuthResponse;
|
|
146
|
+
expect(signInResponse.error).toBeNull();
|
|
147
|
+
|
|
148
|
+
// Verify user data is returned (session details depend on JWT/cookie mode)
|
|
149
|
+
expect(signInResponse.data?.user).toBeDefined();
|
|
150
|
+
expect(signInResponse.data?.user?.email).toBe(testEmail);
|
|
151
|
+
|
|
152
|
+
console.info(' ✓ Sign-in successful, user data received');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// =========================================================================
|
|
156
|
+
// Sign-Out Tests
|
|
157
|
+
// =========================================================================
|
|
158
|
+
|
|
159
|
+
test('7. should sign out successfully', async () => {
|
|
160
|
+
test.skip(!apiAvailable, 'API server not running - skipping demo test');
|
|
161
|
+
|
|
162
|
+
// First sign in
|
|
163
|
+
const signInResponse = (await authClient.signIn.email({
|
|
164
|
+
email: testEmail,
|
|
165
|
+
password: testPassword,
|
|
166
|
+
})) as AuthResponse;
|
|
167
|
+
expect(signInResponse.error).toBeNull();
|
|
168
|
+
|
|
169
|
+
// Then sign out - a Better-Auth client handles cookies internally
|
|
170
|
+
await authClient.signOut();
|
|
171
|
+
|
|
172
|
+
// Sign-out should succeed
|
|
173
|
+
console.info(' ✓ Sign-out completed');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// =========================================================================
|
|
177
|
+
// Full Authentication Flow
|
|
178
|
+
// =========================================================================
|
|
179
|
+
|
|
180
|
+
test('8. should complete full auth flow: sign-up -> sign-in -> sign-out', async () => {
|
|
181
|
+
test.skip(!apiAvailable, 'API server not running - skipping demo test');
|
|
182
|
+
|
|
183
|
+
const flowEmail = `flow-${Date.now()}@iam-test.com`;
|
|
184
|
+
const flowPassword = `FlowPass${Date.now()}!`;
|
|
185
|
+
const flowName = 'Flow Test User';
|
|
186
|
+
|
|
187
|
+
// Step 1: Sign Up
|
|
188
|
+
const signUpRes = (await authClient.signUp.email({
|
|
189
|
+
email: flowEmail,
|
|
190
|
+
name: flowName,
|
|
191
|
+
password: flowPassword,
|
|
192
|
+
})) as AuthResponse;
|
|
193
|
+
expect(signUpRes.error).toBeNull();
|
|
194
|
+
expect(signUpRes.data?.user?.email).toBe(flowEmail);
|
|
195
|
+
console.info(' ✓ Step 1: Sign-up successful');
|
|
196
|
+
|
|
197
|
+
// Step 2: Sign In
|
|
198
|
+
const signInRes = (await authClient.signIn.email({
|
|
199
|
+
email: flowEmail,
|
|
200
|
+
password: flowPassword,
|
|
201
|
+
})) as AuthResponse;
|
|
202
|
+
expect(signInRes.error).toBeNull();
|
|
203
|
+
expect(signInRes.data?.user?.email).toBe(flowEmail);
|
|
204
|
+
expect(signInRes.data?.user).toBeDefined();
|
|
205
|
+
console.info(' ✓ Step 2: Sign-in successful');
|
|
206
|
+
|
|
207
|
+
// Step 3: Sign Out
|
|
208
|
+
await authClient.signOut();
|
|
209
|
+
console.info(' ✓ Step 3: Sign-out successful');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// =========================================================================
|
|
213
|
+
// Response Structure Tests
|
|
214
|
+
// =========================================================================
|
|
215
|
+
|
|
216
|
+
test('9. sign-in response should have correct structure', async () => {
|
|
217
|
+
test.skip(!apiAvailable, 'API server not running - skipping demo test');
|
|
218
|
+
|
|
219
|
+
const response = (await authClient.signIn.email({
|
|
220
|
+
email: testEmail,
|
|
221
|
+
password: testPassword,
|
|
222
|
+
})) as AuthResponse;
|
|
223
|
+
|
|
224
|
+
expect(response.error).toBeNull();
|
|
225
|
+
expect(response.data).toBeDefined();
|
|
226
|
+
expect(response.data?.user).toBeDefined();
|
|
227
|
+
expect(response.data?.user?.id).toBeDefined();
|
|
228
|
+
expect(response.data?.user?.email).toBe(testEmail);
|
|
229
|
+
// User object should have core fields
|
|
230
|
+
expect(response.data?.user?.name).toBeDefined();
|
|
231
|
+
console.info(' ✓ Response structure validated');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test('10. error response should have correct structure', async () => {
|
|
235
|
+
test.skip(!apiAvailable, 'API server not running - skipping demo test');
|
|
236
|
+
|
|
237
|
+
const response = (await authClient.signIn.email({
|
|
238
|
+
email: 'invalid@iam-test.com',
|
|
239
|
+
password: 'wrongPassword',
|
|
240
|
+
})) as AuthResponse;
|
|
241
|
+
|
|
242
|
+
expect(response.error).not.toBeNull();
|
|
243
|
+
expect(response.error?.message).toBeDefined();
|
|
244
|
+
expect(response.error?.status).toBeDefined();
|
|
245
|
+
console.info(' ✓ Error response structure validated');
|
|
246
|
+
});
|
|
247
|
+
});
|
package/package.json
CHANGED
|
@@ -1,30 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-nuxt-base",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Starter to generate a configured environment with VueJS, Nuxt, Tailwind, Eslint,
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"npm": ">=10"
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Starter to generate a configured environment with VueJS, Nuxt, Tailwind, Eslint, Unit Tests, Playwright etc.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"url": "https://github.com/lenneTech/nuxt-base-starter"
|
|
9
8
|
},
|
|
9
|
+
"author": "lenne.Tech GmbH",
|
|
10
10
|
"bin": {
|
|
11
11
|
"create-nuxt-base": "./index.js"
|
|
12
12
|
},
|
|
13
|
+
"main": "index.js",
|
|
13
14
|
"scripts": {
|
|
14
|
-
"
|
|
15
|
-
"format": "
|
|
15
|
+
"format": "oxfmt",
|
|
16
|
+
"format:check": "oxfmt --check",
|
|
16
17
|
"release": "standard-version && git push --follow-tags origin main",
|
|
17
18
|
"release:minor": "standard-version --release-as minor && git push --follow-tags origin main",
|
|
18
19
|
"release:major": "standard-version --release-as major && git push --follow-tags origin main"
|
|
19
20
|
},
|
|
20
|
-
"author": "lenne.Tech GmbH",
|
|
21
|
-
"license": "MIT",
|
|
22
21
|
"dependencies": {
|
|
23
22
|
"cross-spawn": "7.0.6",
|
|
24
|
-
"fs-extra": "11.3.
|
|
23
|
+
"fs-extra": "11.3.3"
|
|
25
24
|
},
|
|
26
25
|
"devDependencies": {
|
|
27
|
-
"
|
|
26
|
+
"oxfmt": "latest",
|
|
28
27
|
"standard-version": "9.5.0"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22",
|
|
31
|
+
"npm": ">=10"
|
|
29
32
|
}
|
|
30
33
|
}
|
package/.eslintignore
DELETED
package/.eslintrc
DELETED
package/.prettierignore
DELETED
package/.prettierrc
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const contextMenuState = () => useState<any | null>(() => null);
|
|
2
|
-
|
|
3
|
-
export function useContextMenu() {
|
|
4
|
-
const menu = contextMenuState();
|
|
5
|
-
|
|
6
|
-
const open = (config: { items: any[] }) => {
|
|
7
|
-
menu.value = Object.assign(config, { show: true });
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const close = () => {
|
|
11
|
-
menu.value = null;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
return {
|
|
15
|
-
activeMenu: menu,
|
|
16
|
-
close,
|
|
17
|
-
open,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
export function useFormHelper() {
|
|
2
|
-
const { close, open } = useModal();
|
|
3
|
-
|
|
4
|
-
function onReload(dirty: boolean, event: BeforeUnloadEvent) {
|
|
5
|
-
if (dirty) {
|
|
6
|
-
event.preventDefault();
|
|
7
|
-
event.returnValue = '';
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async function onNavigate(dirty: boolean) {
|
|
12
|
-
if (dirty) {
|
|
13
|
-
const answer = await openConfirmModal();
|
|
14
|
-
if (!answer) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function openConfirmModal() {
|
|
22
|
-
return new Promise((resolve) => {
|
|
23
|
-
open({
|
|
24
|
-
component: 'ModalConfirm', // TODO: Replace with correct component
|
|
25
|
-
confirm: (confirmed) => {
|
|
26
|
-
resolve(confirmed);
|
|
27
|
-
close();
|
|
28
|
-
},
|
|
29
|
-
data: {
|
|
30
|
-
text: 'Sind Sie sich sicher, dass Sie den Vorgang abbrechen möchten?\n' + 'Es befinden sich ungesicherte Daten auf dieser Seite.',
|
|
31
|
-
title: 'Vorsicht, ungesicherte Daten!',
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
onNavigate,
|
|
39
|
-
onReload,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
export interface ModalContext<T = object> {
|
|
2
|
-
closable?: boolean;
|
|
3
|
-
component: any;
|
|
4
|
-
confirm?: (confirmed: boolean) => void;
|
|
5
|
-
data?: T;
|
|
6
|
-
show: boolean;
|
|
7
|
-
showInner: boolean;
|
|
8
|
-
size?: 'auto' | 'lg' | 'md' | 'sm';
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface Modal<T> {
|
|
12
|
-
closable?: boolean;
|
|
13
|
-
component: any;
|
|
14
|
-
confirm?: (confirmed: boolean) => void;
|
|
15
|
-
data?: T;
|
|
16
|
-
size?: 'auto' | 'lg' | 'md' | 'sm';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const modalState = <T>() => useState<ModalContext<T> | null>(() => null);
|
|
20
|
-
|
|
21
|
-
export function useModal<T = object>() {
|
|
22
|
-
const modal = modalState<T>();
|
|
23
|
-
|
|
24
|
-
const open = (modalConfig: Modal<T>) => {
|
|
25
|
-
let delay = 0;
|
|
26
|
-
if (modal.value) {
|
|
27
|
-
delay = 130;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const data = Object.assign(modalConfig, { show: false, showInner: false }) as ModalContext<T>;
|
|
31
|
-
data.size ??= 'md';
|
|
32
|
-
data.closable ??= true;
|
|
33
|
-
|
|
34
|
-
setTimeout(() => {
|
|
35
|
-
modal.value = data;
|
|
36
|
-
}, delay);
|
|
37
|
-
|
|
38
|
-
// Disable scrolling of background
|
|
39
|
-
document.body.style.overflowY = 'hidden';
|
|
40
|
-
|
|
41
|
-
setTimeout(() => {
|
|
42
|
-
if (modal.value) {
|
|
43
|
-
modal.value.show = true;
|
|
44
|
-
}
|
|
45
|
-
}, 30 + delay);
|
|
46
|
-
|
|
47
|
-
setTimeout(() => {
|
|
48
|
-
if (modal.value) {
|
|
49
|
-
modal.value.showInner = true;
|
|
50
|
-
}
|
|
51
|
-
}, 100 + delay);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const close = (duration?: number) => {
|
|
55
|
-
let animationDuration = duration;
|
|
56
|
-
|
|
57
|
-
if (typeof animationDuration !== 'number') {
|
|
58
|
-
animationDuration = 100;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (modal.value) {
|
|
62
|
-
modal.value.showInner = false;
|
|
63
|
-
|
|
64
|
-
setTimeout(() => {
|
|
65
|
-
if (modal.value) {
|
|
66
|
-
modal.value.show = false;
|
|
67
|
-
}
|
|
68
|
-
}, 30 + animationDuration);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Deactivate scrolling of background
|
|
72
|
-
document.body.style.overflowY = '';
|
|
73
|
-
|
|
74
|
-
setTimeout(() => {
|
|
75
|
-
modal.value = null;
|
|
76
|
-
}, 130 + animationDuration);
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
activeModal: modal,
|
|
81
|
-
close,
|
|
82
|
-
open,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'uncrypto';
|
|
2
|
-
|
|
3
|
-
interface Notification {
|
|
4
|
-
duration?: number;
|
|
5
|
-
text?: string;
|
|
6
|
-
title: string;
|
|
7
|
-
type: 'error' | 'info' | 'success' | 'warning';
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const notificationState = () => useState<Array<Notification & { uuid: string }>>(() => []);
|
|
11
|
-
|
|
12
|
-
export function useNotification() {
|
|
13
|
-
const notifications = notificationState();
|
|
14
|
-
const notify = (message: Notification) => {
|
|
15
|
-
const data = Object.assign(message, { uuid: randomUUID() });
|
|
16
|
-
data.duration ??= 5000;
|
|
17
|
-
notifications.value.push(data);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const remove = (uuid: string) => {
|
|
21
|
-
notifications.value = notifications.value.filter((n) => n.uuid !== uuid);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
notifications,
|
|
26
|
-
notify,
|
|
27
|
-
remove,
|
|
28
|
-
};
|
|
29
|
-
}
|