picocrank 1.9.1 → 1.11.0
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/package.json +8 -8
- package/router.js +47 -0
- package/vue/components/Header.vue +4 -0
- package/vue/components/Login.vue +279 -0
- package/vue/components/Navigation.vue +167 -0
- package/vue/components/NavigationGrid.vue +294 -0
- package/vue/components/Sidebar.vue +6 -136
- package/vue/components/Table.vue +5 -1
- package/vue/components/Tabs.vue +233 -0
- package/vue/views/Admin.vue +14 -14
- package/vue/views/App.vue +36 -21
- package/vue/views/FormExample.vue +268 -0
- package/vue/views/Login.vue +65 -0
- package/vue/views/LoginExample.vue +117 -0
- package/vue/views/TabsExample.vue +337 -0
- package/vue/views/UserControlPanel.vue +79 -0
- package/vue/views/Welcome.vue +1 -1
package/vue/views/Admin.vue
CHANGED
|
@@ -9,29 +9,29 @@
|
|
|
9
9
|
<h3>System Overview</h3>
|
|
10
10
|
<p>Welcome to the administrative panel. This is a placeholder for administrative functionality.</p>
|
|
11
11
|
|
|
12
|
-
<div class="
|
|
13
|
-
<div class="stat-
|
|
12
|
+
<div class="grid-boxed">
|
|
13
|
+
<div class="stat-display">
|
|
14
14
|
<h4>Total Users</h4>
|
|
15
|
-
<div class="stat-
|
|
16
|
-
<div class="
|
|
15
|
+
<div class="stat fg-good">1,247</div>
|
|
16
|
+
<div class="subtle">+12% from last month</div>
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
|
-
<div class="stat-
|
|
19
|
+
<div class="stat-display">
|
|
20
20
|
<h4>Active Sessions</h4>
|
|
21
|
-
<div class="stat-
|
|
22
|
-
<div class="
|
|
21
|
+
<div class="stat fg-bad">89</div>
|
|
22
|
+
<div class="subtle">-3% from last hour</div>
|
|
23
23
|
</div>
|
|
24
24
|
|
|
25
|
-
<div class="stat-
|
|
25
|
+
<div class="stat-display">
|
|
26
26
|
<h4>System Load</h4>
|
|
27
|
-
<div class="stat-
|
|
28
|
-
<div class="
|
|
27
|
+
<div class="stat fg-warning">67%</div>
|
|
28
|
+
<div class="subtle">Moderate</div>
|
|
29
29
|
</div>
|
|
30
30
|
|
|
31
|
-
<div class="stat-
|
|
31
|
+
<div class="stat-display">
|
|
32
32
|
<h4>Storage Used</h4>
|
|
33
|
-
<div class="stat-
|
|
34
|
-
<div class="
|
|
33
|
+
<div class="stat fg-good">2.4 GB</div>
|
|
34
|
+
<div class="subtle">45% of total</div>
|
|
35
35
|
</div>
|
|
36
36
|
</div>
|
|
37
37
|
</div>
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
|
|
65
65
|
<div class="admin-section">
|
|
66
66
|
<h3>Quick Actions</h3>
|
|
67
|
-
<div
|
|
67
|
+
<div role = "toolbar">
|
|
68
68
|
<button class="action-btn">Create User</button>
|
|
69
69
|
<button class="action-btn">System Backup</button>
|
|
70
70
|
<button class="action-btn">View Logs</button>
|
package/vue/views/App.vue
CHANGED
|
@@ -10,33 +10,37 @@
|
|
|
10
10
|
</template>
|
|
11
11
|
</Header>
|
|
12
12
|
|
|
13
|
-
<
|
|
14
|
-
<
|
|
13
|
+
<Navigation ref="navigation">
|
|
14
|
+
<div id = "layout">
|
|
15
|
+
<Sidebar v-if="sidebarEnabled" ref = "sidebar" />
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
<div id = "content">
|
|
18
|
+
<main>
|
|
19
|
+
<router-view />
|
|
20
|
+
</main>
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
<footer>
|
|
23
|
+
<span><a href = "https://github.com/jamesread/picocrank">PicoCrank</a></span>
|
|
24
|
+
</footer>
|
|
25
|
+
</div>
|
|
24
26
|
</div>
|
|
25
|
-
</
|
|
27
|
+
</Navigation>
|
|
26
28
|
</template>
|
|
27
29
|
|
|
28
30
|
<script setup>
|
|
29
31
|
import { ref, onMounted, provide } from 'vue';
|
|
30
32
|
import { useRouter } from 'vue-router';
|
|
31
|
-
import { Pin02Icon } from '@hugeicons/core-free-icons'
|
|
33
|
+
import { Pin02Icon, SecurityValidationIcon, EditIcon, ViewIcon, HomeIcon } from '@hugeicons/core-free-icons'
|
|
32
34
|
|
|
33
35
|
import QuickSearch from './../components/QuickSearch.vue'
|
|
36
|
+
import Navigation from './../components/Navigation.vue'
|
|
34
37
|
import '../../styles.css'
|
|
35
38
|
import logoUrl from '/logo.png';
|
|
36
39
|
|
|
37
40
|
const quickSearchRef = ref(null)
|
|
38
41
|
const router = useRouter();
|
|
39
42
|
const sidebar = ref(null);
|
|
43
|
+
const navigation = ref(null);
|
|
40
44
|
const sidebarEnabled = ref(true);
|
|
41
45
|
|
|
42
46
|
function toggleSidebar() {
|
|
@@ -54,17 +58,28 @@
|
|
|
54
58
|
provide('toggleSidebarEnabled', toggleSidebarEnabled);
|
|
55
59
|
|
|
56
60
|
onMounted(() => {
|
|
61
|
+
if (navigation.value) {
|
|
62
|
+
navigation.value.addRouterLink('Welcome')
|
|
63
|
+
navigation.value.addRouterLink('TableExample')
|
|
64
|
+
navigation.value.addRouterLink('CalendarExample')
|
|
65
|
+
navigation.value.addRouterLink('FormExample')
|
|
66
|
+
navigation.value.addRouterLink('TabsExample')
|
|
67
|
+
navigation.value.addSeparator('separator-1');
|
|
68
|
+
navigation.value.addRouterLink('ViewItem', 'View Awesome Item', { params: { id: 1 } })
|
|
69
|
+
navigation.value.addSeparator('separator-2');
|
|
70
|
+
navigation.value.addCallback('Callback Example', helloWorld, { icon: Pin02Icon })
|
|
71
|
+
navigation.value.addSeparator('separator-3');
|
|
72
|
+
navigation.value.addHtml('<h2 style = "padding: 0.75em;">Authentication</h2>', { name: 'auth-heading' })
|
|
73
|
+
navigation.value.addRouterLink('LoginExample')
|
|
74
|
+
navigation.value.addSeparator('separator-4');
|
|
75
|
+
navigation.value.addHtml('<h2 style = "padding: 0.75em;">User Control Panel</h2>', { name: 'user-panel-heading' })
|
|
76
|
+
navigation.value.addRouterLink('UserControlPanel')
|
|
77
|
+
navigation.value.addSeparator('separator-5');
|
|
78
|
+
navigation.value.addHtml('<h2 style = "padding: 0.75em;">Administration</h2>', { name: 'admin-heading' })
|
|
79
|
+
navigation.value.addRouterLink('Admin')
|
|
80
|
+
}
|
|
81
|
+
|
|
57
82
|
if (sidebar.value) {
|
|
58
|
-
sidebar.value.addRouterLink('Welcome')
|
|
59
|
-
sidebar.value.addRouterLink('TableExample')
|
|
60
|
-
sidebar.value.addRouterLink('CalendarExample')
|
|
61
|
-
sidebar.value.addSeparator('separator-1');
|
|
62
|
-
sidebar.value.addRouterLink('ViewItem', 'View Awesome Item', { params: { id: 1 } })
|
|
63
|
-
sidebar.value.addSeparator('separator-2');
|
|
64
|
-
sidebar.value.addCallback('Callback Example', helloWorld, { icon: Pin02Icon })
|
|
65
|
-
sidebar.value.addSeparator('separator-3');
|
|
66
|
-
sidebar.value.addHtml('<h2 style = "padding: 0.75em;">Administration</h2>', { name: 'admin-heading' })
|
|
67
|
-
sidebar.value.addRouterLink('Admin')
|
|
68
83
|
sidebar.value.open();
|
|
69
84
|
sidebar.value.stick();
|
|
70
85
|
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Section
|
|
3
|
+
title="Form Example"
|
|
4
|
+
subtitle="A simple form with various input types"
|
|
5
|
+
>
|
|
6
|
+
<template #toolbar>
|
|
7
|
+
<button @click="resetForm">Reset</button>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<form @submit.prevent="handleSubmit">
|
|
11
|
+
<div class="form-group">
|
|
12
|
+
<label for="name">Name</label>
|
|
13
|
+
<input
|
|
14
|
+
id="name"
|
|
15
|
+
v-model="formData.name"
|
|
16
|
+
type="text"
|
|
17
|
+
placeholder="Enter your name"
|
|
18
|
+
required
|
|
19
|
+
/>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div class="form-group">
|
|
23
|
+
<label for="email">Email</label>
|
|
24
|
+
<input
|
|
25
|
+
id="email"
|
|
26
|
+
v-model="formData.email"
|
|
27
|
+
type="email"
|
|
28
|
+
placeholder="Enter your email"
|
|
29
|
+
required
|
|
30
|
+
/>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="form-group">
|
|
34
|
+
<label for="country">Country</label>
|
|
35
|
+
<select id="country" v-model="formData.country" required>
|
|
36
|
+
<option value="">Select a country</option>
|
|
37
|
+
<option value="us">United States</option>
|
|
38
|
+
<option value="uk">United Kingdom</option>
|
|
39
|
+
<option value="ca">Canada</option>
|
|
40
|
+
<option value="au">Australia</option>
|
|
41
|
+
<option value="de">Germany</option>
|
|
42
|
+
<option value="fr">France</option>
|
|
43
|
+
</select>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div class="form-group">
|
|
47
|
+
<label for="message">Message</label>
|
|
48
|
+
<textarea
|
|
49
|
+
id="message"
|
|
50
|
+
v-model="formData.message"
|
|
51
|
+
placeholder="Enter your message"
|
|
52
|
+
rows="4"
|
|
53
|
+
></textarea>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div class="form-group">
|
|
57
|
+
<label>
|
|
58
|
+
<input
|
|
59
|
+
type="checkbox"
|
|
60
|
+
v-model="formData.newsletter"
|
|
61
|
+
/>
|
|
62
|
+
Subscribe to newsletter
|
|
63
|
+
</label>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div class="form-group">
|
|
67
|
+
<label>Preferred Contact Method</label>
|
|
68
|
+
<div class="radio-group">
|
|
69
|
+
<label>
|
|
70
|
+
<input
|
|
71
|
+
type="radio"
|
|
72
|
+
v-model="formData.contactMethod"
|
|
73
|
+
value="email"
|
|
74
|
+
/>
|
|
75
|
+
Email
|
|
76
|
+
</label>
|
|
77
|
+
<label>
|
|
78
|
+
<input
|
|
79
|
+
type="radio"
|
|
80
|
+
v-model="formData.contactMethod"
|
|
81
|
+
value="phone"
|
|
82
|
+
/>
|
|
83
|
+
Phone
|
|
84
|
+
</label>
|
|
85
|
+
<label>
|
|
86
|
+
<input
|
|
87
|
+
type="radio"
|
|
88
|
+
v-model="formData.contactMethod"
|
|
89
|
+
value="mail"
|
|
90
|
+
/>
|
|
91
|
+
Mail
|
|
92
|
+
</label>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="form-actions">
|
|
97
|
+
<button type="submit">Submit</button>
|
|
98
|
+
<button type="button" @click="resetForm">Clear</button>
|
|
99
|
+
</div>
|
|
100
|
+
</form>
|
|
101
|
+
|
|
102
|
+
<div v-if="submitted" class="form-result">
|
|
103
|
+
<h3>Form Submitted!</h3>
|
|
104
|
+
<pre>{{ JSON.stringify(formData, null, 2) }}</pre>
|
|
105
|
+
</div>
|
|
106
|
+
</Section>
|
|
107
|
+
</template>
|
|
108
|
+
|
|
109
|
+
<script setup>
|
|
110
|
+
import { ref } from 'vue'
|
|
111
|
+
import Section from '../components/Section.vue'
|
|
112
|
+
|
|
113
|
+
const formData = ref({
|
|
114
|
+
name: '',
|
|
115
|
+
email: '',
|
|
116
|
+
country: '',
|
|
117
|
+
message: '',
|
|
118
|
+
newsletter: false,
|
|
119
|
+
contactMethod: 'email'
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
const submitted = ref(false)
|
|
123
|
+
|
|
124
|
+
function handleSubmit() {
|
|
125
|
+
submitted.value = true
|
|
126
|
+
console.log('Form submitted:', formData.value)
|
|
127
|
+
// In a real application, you would send this data to a server
|
|
128
|
+
alert('Form submitted! Check the console and the form result below.')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function resetForm() {
|
|
132
|
+
formData.value = {
|
|
133
|
+
name: '',
|
|
134
|
+
email: '',
|
|
135
|
+
country: '',
|
|
136
|
+
message: '',
|
|
137
|
+
newsletter: false,
|
|
138
|
+
contactMethod: 'email'
|
|
139
|
+
}
|
|
140
|
+
submitted.value = false
|
|
141
|
+
}
|
|
142
|
+
</script>
|
|
143
|
+
|
|
144
|
+
<style scoped>
|
|
145
|
+
.form-group {
|
|
146
|
+
margin-bottom: 1.5rem;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.form-group label {
|
|
150
|
+
display: block;
|
|
151
|
+
margin-bottom: 0.5rem;
|
|
152
|
+
font-weight: 500;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.form-group input[type="text"],
|
|
156
|
+
.form-group input[type="email"],
|
|
157
|
+
.form-group select,
|
|
158
|
+
.form-group textarea {
|
|
159
|
+
width: 100%;
|
|
160
|
+
padding: 0.5rem;
|
|
161
|
+
border: 1px solid #ccc;
|
|
162
|
+
border-radius: 0.25rem;
|
|
163
|
+
font-size: 1rem;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.form-group textarea {
|
|
167
|
+
resize: vertical;
|
|
168
|
+
font-family: inherit;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.form-group input[type="checkbox"],
|
|
172
|
+
.form-group input[type="radio"] {
|
|
173
|
+
margin-right: 0.5rem;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.form-group label:has(input[type="checkbox"]) {
|
|
177
|
+
display: flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
font-weight: normal;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.radio-group {
|
|
183
|
+
display: flex;
|
|
184
|
+
gap: 1.5rem;
|
|
185
|
+
flex-wrap: wrap;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.radio-group label {
|
|
189
|
+
display: flex;
|
|
190
|
+
align-items: center;
|
|
191
|
+
font-weight: normal;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.form-actions {
|
|
195
|
+
display: flex;
|
|
196
|
+
gap: 1rem;
|
|
197
|
+
margin-top: 2rem;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.form-actions button {
|
|
201
|
+
padding: 0.75rem 1.5rem;
|
|
202
|
+
border: none;
|
|
203
|
+
border-radius: 0.25rem;
|
|
204
|
+
font-size: 1rem;
|
|
205
|
+
cursor: pointer;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.form-actions button[type="submit"] {
|
|
209
|
+
background-color: #0366d6;
|
|
210
|
+
color: white;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.form-actions button[type="submit"]:hover {
|
|
214
|
+
background-color: #0256c2;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.form-actions button[type="button"] {
|
|
218
|
+
background-color: #6c757d;
|
|
219
|
+
color: white;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.form-actions button[type="button"]:hover {
|
|
223
|
+
background-color: #5a6268;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.form-result {
|
|
227
|
+
margin-top: 2rem;
|
|
228
|
+
padding: 1rem;
|
|
229
|
+
background-color: #f8f9fa;
|
|
230
|
+
border-radius: 0.25rem;
|
|
231
|
+
border: 1px solid #dee2e6;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.form-result h3 {
|
|
235
|
+
margin-top: 0;
|
|
236
|
+
margin-bottom: 1rem;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.form-result pre {
|
|
240
|
+
margin: 0;
|
|
241
|
+
padding: 1rem;
|
|
242
|
+
background-color: white;
|
|
243
|
+
border-radius: 0.25rem;
|
|
244
|
+
overflow-x: auto;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
@media (prefers-color-scheme: dark) {
|
|
248
|
+
.form-group input[type="text"],
|
|
249
|
+
.form-group input[type="email"],
|
|
250
|
+
.form-group select,
|
|
251
|
+
.form-group textarea {
|
|
252
|
+
background-color: #333;
|
|
253
|
+
border-color: #555;
|
|
254
|
+
color: #fff;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.form-result {
|
|
258
|
+
background-color: #2d2d2d;
|
|
259
|
+
border-color: #444;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.form-result pre {
|
|
263
|
+
background-color: #1a1a1a;
|
|
264
|
+
color: #fff;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
</style>
|
|
268
|
+
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Section title="Login" classes="">
|
|
3
|
+
<Login
|
|
4
|
+
@local-login="handleLocalLogin"
|
|
5
|
+
@oauth-login="handleOAuthLogin"
|
|
6
|
+
@tab-change="onTabChange"
|
|
7
|
+
/>
|
|
8
|
+
</Section>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import { useRouter } from 'vue-router';
|
|
13
|
+
import Section from '../components/Section.vue';
|
|
14
|
+
import Login from '../components/Login.vue';
|
|
15
|
+
|
|
16
|
+
const router = useRouter();
|
|
17
|
+
|
|
18
|
+
// Handle local login
|
|
19
|
+
function handleLocalLogin(credentials) {
|
|
20
|
+
console.log('Local login attempt:', {
|
|
21
|
+
username: credentials.username,
|
|
22
|
+
password: '***' // Don't log passwords in production
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// TODO: Implement actual authentication logic
|
|
26
|
+
// Example: Simulate API call
|
|
27
|
+
// try {
|
|
28
|
+
// const response = await fetch('/api/auth/local', {
|
|
29
|
+
// method: 'POST',
|
|
30
|
+
// headers: { 'Content-Type': 'application/json' },
|
|
31
|
+
// body: JSON.stringify(credentials)
|
|
32
|
+
// });
|
|
33
|
+
//
|
|
34
|
+
// if (response.ok) {
|
|
35
|
+
// const data = await response.json();
|
|
36
|
+
// // Store token, redirect, etc.
|
|
37
|
+
// router.push('/');
|
|
38
|
+
// } else {
|
|
39
|
+
// // Handle error
|
|
40
|
+
// }
|
|
41
|
+
// } catch (error) {
|
|
42
|
+
// // Handle error
|
|
43
|
+
// }
|
|
44
|
+
|
|
45
|
+
// Placeholder success
|
|
46
|
+
alert(`Local login would authenticate: ${credentials.username}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Handle OAuth login
|
|
50
|
+
function handleOAuthLogin(provider) {
|
|
51
|
+
console.log('OAuth login attempt:', provider);
|
|
52
|
+
|
|
53
|
+
// TODO: Implement actual OAuth redirect logic
|
|
54
|
+
// Example: Redirect to OAuth provider
|
|
55
|
+
// window.location.href = provider.authUrl;
|
|
56
|
+
|
|
57
|
+
// Placeholder
|
|
58
|
+
alert(`OAuth login would redirect to: ${provider.name}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Handle tab change
|
|
62
|
+
function onTabChange(tab, tabId) {
|
|
63
|
+
console.log('Tab changed:', tab, tabId);
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Section title="Login Example" subtitle="A reusable login form component with local and OAuth2 authentication">
|
|
3
|
+
<Login
|
|
4
|
+
:oauth-providers="oauthProviders"
|
|
5
|
+
@local-login="handleLocalLogin"
|
|
6
|
+
@oauth-login="handleOAuthLogin"
|
|
7
|
+
@tab-change="onTabChange"
|
|
8
|
+
/>
|
|
9
|
+
</Section>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup>
|
|
13
|
+
import { ref } from 'vue';
|
|
14
|
+
import { useRouter } from 'vue-router';
|
|
15
|
+
import Section from '../components/Section.vue';
|
|
16
|
+
import Login from '../components/Login.vue';
|
|
17
|
+
|
|
18
|
+
const router = useRouter();
|
|
19
|
+
|
|
20
|
+
// OAuth providers configuration
|
|
21
|
+
const oauthProviders = ref([
|
|
22
|
+
{
|
|
23
|
+
id: 'google',
|
|
24
|
+
name: 'Google',
|
|
25
|
+
icon: null, // Add icon from @hugeicons/core-free-icons if available
|
|
26
|
+
class: 'good',
|
|
27
|
+
authUrl: '/api/auth/google'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'github',
|
|
31
|
+
name: 'GitHub',
|
|
32
|
+
icon: null, // Add icon from @hugeicons/core-free-icons if available
|
|
33
|
+
class: 'neutral',
|
|
34
|
+
authUrl: '/api/auth/github'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'microsoft',
|
|
38
|
+
name: 'Microsoft',
|
|
39
|
+
icon: null, // Add icon from @hugeicons/core-free-icons if available
|
|
40
|
+
class: 'good',
|
|
41
|
+
authUrl: '/api/auth/microsoft'
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'facebook',
|
|
45
|
+
name: 'Facebook',
|
|
46
|
+
icon: null, // Add icon from @hugeicons/core-free-icons if available
|
|
47
|
+
class: 'good',
|
|
48
|
+
authUrl: '/api/auth/facebook'
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'twitter',
|
|
52
|
+
name: 'Twitter',
|
|
53
|
+
icon: null, // Add icon from @hugeicons/core-free-icons if available
|
|
54
|
+
class: 'neutral',
|
|
55
|
+
authUrl: '/api/auth/twitter'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'apple',
|
|
59
|
+
name: 'Apple',
|
|
60
|
+
icon: null, // Add icon from @hugeicons/core-free-icons if available
|
|
61
|
+
class: 'neutral',
|
|
62
|
+
authUrl: '/api/auth/apple'
|
|
63
|
+
}
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
// Handle local login
|
|
67
|
+
function handleLocalLogin(credentials) {
|
|
68
|
+
console.log('Local login attempt:', {
|
|
69
|
+
username: credentials.username,
|
|
70
|
+
password: '***' // Don't log passwords in production
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// TODO: Implement actual authentication logic
|
|
74
|
+
// Example: Simulate API call
|
|
75
|
+
// try {
|
|
76
|
+
// const response = await fetch('/api/auth/local', {
|
|
77
|
+
// method: 'POST',
|
|
78
|
+
// headers: { 'Content-Type': 'application/json' },
|
|
79
|
+
// body: JSON.stringify(credentials)
|
|
80
|
+
// });
|
|
81
|
+
//
|
|
82
|
+
// if (response.ok) {
|
|
83
|
+
// const data = await response.json();
|
|
84
|
+
// // Store token, redirect, etc.
|
|
85
|
+
// router.push('/');
|
|
86
|
+
// } else {
|
|
87
|
+
// // Handle error - you can use the component's setLocalLoginError method
|
|
88
|
+
// }
|
|
89
|
+
// } catch (error) {
|
|
90
|
+
// // Handle error
|
|
91
|
+
// }
|
|
92
|
+
|
|
93
|
+
// Placeholder success
|
|
94
|
+
alert(`Local login would authenticate: ${credentials.username}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Handle OAuth login
|
|
98
|
+
function handleOAuthLogin(provider) {
|
|
99
|
+
console.log('OAuth login attempt:', provider);
|
|
100
|
+
|
|
101
|
+
// TODO: Implement actual OAuth redirect logic
|
|
102
|
+
// Example: Redirect to OAuth provider
|
|
103
|
+
// window.location.href = provider.authUrl;
|
|
104
|
+
|
|
105
|
+
// Placeholder
|
|
106
|
+
alert(`OAuth login would redirect to: ${provider.name}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Handle tab change
|
|
110
|
+
function onTabChange(tab, tabId) {
|
|
111
|
+
console.log('Tab changed:', tab, tabId);
|
|
112
|
+
}
|
|
113
|
+
</script>
|
|
114
|
+
|
|
115
|
+
<style scoped>
|
|
116
|
+
/* Additional styles if needed */
|
|
117
|
+
</style>
|