picocrank 1.18.2 → 1.19.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/index.html +10 -0
- package/package.json +1 -1
- package/router.js +43 -12
- package/styles/dark-theme.css +189 -0
- package/styles.css +1 -1
- package/vue/components/Calendar.vue +36 -2
- package/vue/components/Header.vue +28 -2
- package/vue/components/Navigation.vue +11 -0
- package/vue/components/NavigationGrid.vue +2 -2
- package/vue/components/NotificationPopup.vue +118 -0
- package/vue/components/NotificationPopups.vue +73 -0
- package/vue/components/Pagination.vue +1 -1
- package/vue/components/QuickSearch.vue +1 -1
- package/vue/components/Sidebar.vue +185 -31
- package/vue/components/Tabs.vue +1 -1
- package/vue/components/TopBar.vue +4 -2
- package/vue/composables/useNotificationPopups.js +78 -0
- package/vue/composables/useTheme.js +60 -0
- package/vue/views/App.vue +33 -20
- package/vue/views/ButtonsExample.vue +93 -0
- package/vue/views/CalendarExample.vue +26 -7
- package/vue/views/NavigationGridExample.vue +89 -0
- package/vue/views/PageStructure.vue +111 -0
- package/vue/views/StatusExample.vue +273 -0
- package/vue/views/TabsExample.vue +1 -1
- package/vue/views/ViewItem.vue +189 -3
- package/vue/views/Welcome.vue +218 -96
- package/vue/views/UserControlPanel.vue +0 -82
package/vue/views/ViewItem.vue
CHANGED
|
@@ -1,5 +1,191 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
<section class="with-header-and-content">
|
|
3
|
+
<div class="section-header flex-row">
|
|
4
|
+
<div class="fg1">
|
|
5
|
+
<h2 :title="item.subtitle">{{ item.title }}</h2>
|
|
6
|
+
</div>
|
|
7
|
+
<div role="toolbar">
|
|
8
|
+
<router-link :to="{ name: 'TableExample' }" class="button neutral">Back to table</router-link>
|
|
9
|
+
<button type="button" class="good">Edit</button>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div class="section-content padding">
|
|
14
|
+
<p>{{ item.summary }}</p>
|
|
15
|
+
|
|
16
|
+
<dl>
|
|
17
|
+
<dt>ID</dt>
|
|
18
|
+
<dd>{{ id }}</dd>
|
|
19
|
+
|
|
20
|
+
<dt>Name</dt>
|
|
21
|
+
<dd>{{ item.name }}</dd>
|
|
22
|
+
|
|
23
|
+
<dt>Status</dt>
|
|
24
|
+
<dd>
|
|
25
|
+
<span class="tag" :class="item.statusClass">{{ item.status }}</span>
|
|
26
|
+
</dd>
|
|
27
|
+
|
|
28
|
+
<dt>Role</dt>
|
|
29
|
+
<dd>{{ item.role }}</dd>
|
|
30
|
+
|
|
31
|
+
<dt>Location</dt>
|
|
32
|
+
<dd>{{ item.location }}</dd>
|
|
33
|
+
</dl>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="section-subheader">
|
|
37
|
+
<h3>Contact</h3>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div class="section-content padding">
|
|
41
|
+
<dl>
|
|
42
|
+
<dt>Email</dt>
|
|
43
|
+
<dd><a :href="`mailto:${item.email}`">{{ item.email }}</a></dd>
|
|
44
|
+
|
|
45
|
+
<dt>Phone</dt>
|
|
46
|
+
<dd>{{ item.phone }}</dd>
|
|
47
|
+
|
|
48
|
+
<dt>Timezone</dt>
|
|
49
|
+
<dd>{{ item.timezone }}</dd>
|
|
50
|
+
</dl>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div class="section-subheader">
|
|
54
|
+
<h3>Details</h3>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div class="section-content padding">
|
|
58
|
+
<dl>
|
|
59
|
+
<dt>Department</dt>
|
|
60
|
+
<dd>{{ item.department }}</dd>
|
|
61
|
+
|
|
62
|
+
<dt>Manager</dt>
|
|
63
|
+
<dd>{{ item.manager }}</dd>
|
|
64
|
+
|
|
65
|
+
<dt>Start date</dt>
|
|
66
|
+
<dd>{{ item.startDate }}</dd>
|
|
67
|
+
|
|
68
|
+
<dt>Last updated</dt>
|
|
69
|
+
<dd>{{ item.lastUpdated }}</dd>
|
|
70
|
+
</dl>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div class="section-subheader">
|
|
74
|
+
<h3>Recent activity</h3>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div class="section-content padding">
|
|
78
|
+
<ul>
|
|
79
|
+
<li v-for="(entry, index) in item.activity" :key="index">
|
|
80
|
+
<span class="subtle">{{ entry.when }}</span>
|
|
81
|
+
— {{ entry.text }}
|
|
82
|
+
</li>
|
|
83
|
+
</ul>
|
|
84
|
+
</div>
|
|
85
|
+
</section>
|
|
5
86
|
</template>
|
|
87
|
+
|
|
88
|
+
<script setup>
|
|
89
|
+
import { computed } from 'vue'
|
|
90
|
+
|
|
91
|
+
const props = defineProps({
|
|
92
|
+
id: {
|
|
93
|
+
type: [String, Number],
|
|
94
|
+
required: true,
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
const itemsById = {
|
|
99
|
+
1: {
|
|
100
|
+
title: 'Acme deployment',
|
|
101
|
+
subtitle: 'Production release #1842',
|
|
102
|
+
summary: 'Read-only detail view for a single record, using Femtocrank definition lists and section subheaders.',
|
|
103
|
+
name: 'Acme deployment',
|
|
104
|
+
status: 'Active',
|
|
105
|
+
statusClass: 'fg-good',
|
|
106
|
+
role: 'Release',
|
|
107
|
+
location: 'us-east-1',
|
|
108
|
+
email: 'releases@example.com',
|
|
109
|
+
phone: '+1 555 0100',
|
|
110
|
+
timezone: 'UTC',
|
|
111
|
+
department: 'Platform engineering',
|
|
112
|
+
manager: 'Alex Morgan',
|
|
113
|
+
startDate: '12 March 2026',
|
|
114
|
+
lastUpdated: '9 July 2026, 09:14',
|
|
115
|
+
activity: [
|
|
116
|
+
{ when: '2 hours ago', text: 'Health check passed for all regions.' },
|
|
117
|
+
{ when: 'Yesterday', text: 'Configuration reviewed by on-call engineer.' },
|
|
118
|
+
{ when: '3 days ago', text: 'Deployed from build 1842.' },
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
Alice: {
|
|
122
|
+
title: 'Alice',
|
|
123
|
+
subtitle: 'Senior analyst',
|
|
124
|
+
summary: 'Example person record linked from the table example.',
|
|
125
|
+
name: 'Alice',
|
|
126
|
+
status: 'Active',
|
|
127
|
+
statusClass: 'fg-good',
|
|
128
|
+
role: 'Senior analyst',
|
|
129
|
+
location: 'New York',
|
|
130
|
+
email: 'alice@example.com',
|
|
131
|
+
phone: '+1 555 0142',
|
|
132
|
+
timezone: 'America/New_York',
|
|
133
|
+
department: 'Operations',
|
|
134
|
+
manager: 'Jordan Lee',
|
|
135
|
+
startDate: '4 June 2022',
|
|
136
|
+
lastUpdated: '8 July 2026, 16:30',
|
|
137
|
+
activity: [
|
|
138
|
+
{ when: 'Today', text: 'Approved quarterly forecast submission.' },
|
|
139
|
+
{ when: '2 days ago', text: 'Updated territory assignment.' },
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
Bob: {
|
|
143
|
+
title: 'Bob',
|
|
144
|
+
subtitle: 'Support specialist',
|
|
145
|
+
summary: 'Example person record linked from the table example.',
|
|
146
|
+
name: 'Bob',
|
|
147
|
+
status: 'Away',
|
|
148
|
+
statusClass: 'fg-warning',
|
|
149
|
+
role: 'Support specialist',
|
|
150
|
+
location: 'Los Angeles',
|
|
151
|
+
email: 'bob@example.com',
|
|
152
|
+
phone: '+1 555 0198',
|
|
153
|
+
timezone: 'America/Los_Angeles',
|
|
154
|
+
department: 'Customer success',
|
|
155
|
+
manager: 'Sam Patel',
|
|
156
|
+
startDate: '19 January 2024',
|
|
157
|
+
lastUpdated: '5 July 2026, 11:02',
|
|
158
|
+
activity: [
|
|
159
|
+
{ when: '4 hours ago', text: 'Set status to away.' },
|
|
160
|
+
{ when: 'Yesterday', text: 'Closed ticket #8821.' },
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const item = computed(() => {
|
|
166
|
+
const key = String(props.id)
|
|
167
|
+
if (itemsById[key]) {
|
|
168
|
+
return itemsById[key]
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const label = key.charAt(0).toUpperCase() + key.slice(1)
|
|
172
|
+
return {
|
|
173
|
+
title: label,
|
|
174
|
+
subtitle: `Record ${key}`,
|
|
175
|
+
summary: 'Generic detail view for records opened from the table or navigation.',
|
|
176
|
+
name: label,
|
|
177
|
+
status: 'Unknown',
|
|
178
|
+
statusClass: 'fg-note',
|
|
179
|
+
role: '—',
|
|
180
|
+
location: '—',
|
|
181
|
+
email: `${key.toLowerCase()}@example.com`,
|
|
182
|
+
phone: '—',
|
|
183
|
+
timezone: 'UTC',
|
|
184
|
+
department: '—',
|
|
185
|
+
manager: '—',
|
|
186
|
+
startDate: '—',
|
|
187
|
+
lastUpdated: '—',
|
|
188
|
+
activity: [{ when: '—', text: 'No activity recorded for this item.' }],
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
</script>
|
package/vue/views/Welcome.vue
CHANGED
|
@@ -1,114 +1,236 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Section
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
</button>
|
|
12
|
-
<div style="display: flex; gap: 1rem; align-items: center;">
|
|
13
|
-
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
|
|
14
|
-
<input type="checkbox" :checked="quickSearchEnabled" @change="toggleQuickSearchEnabled" />
|
|
15
|
-
<span>Enable QuickSearch</span>
|
|
16
|
-
</label>
|
|
17
|
-
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
|
|
18
|
-
<input type="checkbox" :checked="topBarEnabled" @change="toggleTopBarEnabled" />
|
|
19
|
-
<span>Enable TopBar</span>
|
|
20
|
-
</label>
|
|
2
|
+
<Section
|
|
3
|
+
id="welcome"
|
|
4
|
+
subtitle="Vue components on the Femtocrank theme"
|
|
5
|
+
classes="welcome-hero-section"
|
|
6
|
+
>
|
|
7
|
+
<template #title>
|
|
8
|
+
<div class="welcome-hero-title">
|
|
9
|
+
<img :src="logoUrl" alt="" class="welcome-logo" />
|
|
10
|
+
<span>Welcome to PicoCrank</span>
|
|
21
11
|
</div>
|
|
22
|
-
<button type="button" @click="toggleBreadcrumbsEnabled" :class="breadcrumbsEnabled ? 'neutral' : 'good'">
|
|
23
|
-
{{ breadcrumbsEnabled ? 'Hide Breadcrumbs' : 'Show Breadcrumbs' }}
|
|
24
|
-
</button>
|
|
25
12
|
</template>
|
|
26
|
-
<p>
|
|
27
|
-
Welcome to <strong>PicoCrank</strong>! This is a flexible example application to showcase a modular Vue 3 UI with a focus on ease of navigation, live UI controls, and modern design elements. Use the toolbar above to interactively enable or disable interface features like the sidebar, branding, QuickSearch, the top navigation bar, and route breadcrumbs in the header.
|
|
28
|
-
</p>
|
|
29
|
-
<p>
|
|
30
|
-
Explore the sections below to see reusable components in action, including toolbars, buttons, and layout options. PicoCrank is designed to be a starting point for your own rapid Vue projects—inspect the code and try customizing the UI!
|
|
31
|
-
</p>
|
|
32
|
-
|
|
33
|
-
</Section>
|
|
34
13
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Highlight</button>
|
|
40
|
-
<button>Quote</button>
|
|
41
|
-
<button class = "good">Add</button>
|
|
42
|
-
</template>
|
|
14
|
+
<p class="welcome-lead">
|
|
15
|
+
Build admin-style UIs quickly with ready-made Vue components and a minimal,
|
|
16
|
+
accessible CSS theme. Browse examples by how you would use them in a real app.
|
|
17
|
+
</p>
|
|
43
18
|
|
|
44
|
-
<div role
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
</
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
19
|
+
<div role="toolbar" class="welcome-actions">
|
|
20
|
+
<router-link :to="{ name: 'PageStructure' }" class="button good">
|
|
21
|
+
Configure layout
|
|
22
|
+
</router-link>
|
|
23
|
+
<a href="#start-here" class="button neutral">Browse components</a>
|
|
24
|
+
<a
|
|
25
|
+
href="https://github.com/jamesread/picocrank"
|
|
26
|
+
class="button neutral"
|
|
27
|
+
target="_blank"
|
|
28
|
+
rel="noopener noreferrer"
|
|
29
|
+
>
|
|
30
|
+
View on GitHub
|
|
31
|
+
</a>
|
|
54
32
|
</div>
|
|
55
33
|
|
|
56
|
-
<p>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
|
60
|
-
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
34
|
+
<p class="subtle">
|
|
35
|
+
New here? Open <router-link :to="{ name: 'PageStructure' }">Page structure</router-link>
|
|
36
|
+
to toggle the sidebar, header branding, QuickSearch, and more.
|
|
61
37
|
</p>
|
|
62
38
|
</Section>
|
|
63
39
|
|
|
64
|
-
<Section
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
The heart-ache and the thousand natural shocks
|
|
73
|
-
That flesh is heir to, 'tis a consummation
|
|
74
|
-
Devoutly to be wish'd. To die, to sleep;
|
|
75
|
-
To sleep: perchance to dream: ay, there's the rub;
|
|
76
|
-
For in that sleep of death what dreams may come
|
|
77
|
-
When we have shuffled off this mortal coil,
|
|
78
|
-
Must give us pause: there's the respect
|
|
79
|
-
That makes calamity of so long life;
|
|
80
|
-
For who would bear the whips and scorns of time,
|
|
81
|
-
The oppressor's wrong
|
|
82
|
-
</p>
|
|
40
|
+
<Section
|
|
41
|
+
id="start-here"
|
|
42
|
+
title="Start here"
|
|
43
|
+
subtitle="Jump into the example areas"
|
|
44
|
+
>
|
|
45
|
+
<Navigation ref="welcomeNavigation">
|
|
46
|
+
<NavigationGrid />
|
|
47
|
+
</Navigation>
|
|
83
48
|
</Section>
|
|
84
49
|
|
|
85
|
-
<Section
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
50
|
+
<Section
|
|
51
|
+
title="Popular examples"
|
|
52
|
+
subtitle="A few good places to begin"
|
|
53
|
+
>
|
|
54
|
+
<div class="grid-boxed welcome-popular-grid">
|
|
55
|
+
<router-link
|
|
56
|
+
v-for="example in popularExamples"
|
|
57
|
+
:key="example.name"
|
|
58
|
+
:to="{ name: example.name }"
|
|
59
|
+
class="stat-display welcome-popular-card"
|
|
60
|
+
>
|
|
61
|
+
<span class="subtle">{{ example.category }}</span>
|
|
62
|
+
<span class="stat">{{ example.label }}</span>
|
|
63
|
+
<span class="welcome-popular-description">{{ example.description }}</span>
|
|
64
|
+
</router-link>
|
|
65
|
+
</div>
|
|
92
66
|
</Section>
|
|
93
67
|
|
|
68
|
+
<Section
|
|
69
|
+
title="Try it"
|
|
70
|
+
subtitle="Small interactions you can trigger right now"
|
|
71
|
+
>
|
|
72
|
+
<dl>
|
|
73
|
+
<dt>Notification popup</dt>
|
|
74
|
+
<dd>
|
|
75
|
+
<button type="button" class="good" @click="showWelcomeToast">
|
|
76
|
+
Show a toast
|
|
77
|
+
</button>
|
|
78
|
+
<p class="subtle">Corner notifications stack, auto-dismiss, and can link elsewhere.</p>
|
|
79
|
+
</dd>
|
|
80
|
+
|
|
81
|
+
<dt>Light and dark mode</dt>
|
|
82
|
+
<dd>
|
|
83
|
+
<p>
|
|
84
|
+
Use the sun/moon button in the header to switch themes. Your preference is
|
|
85
|
+
remembered across visits.
|
|
86
|
+
</p>
|
|
87
|
+
</dd>
|
|
88
|
+
|
|
89
|
+
<dt>Femtocrank theme</dt>
|
|
90
|
+
<dd>
|
|
91
|
+
<p>
|
|
92
|
+
PicoCrank is built on
|
|
93
|
+
<a href="https://github.com/jamesread/femtocrank" target="_blank" rel="noopener noreferrer">Femtocrank</a>
|
|
94
|
+
— zero-dependency CSS with high accessibility defaults.
|
|
95
|
+
</p>
|
|
96
|
+
</dd>
|
|
97
|
+
</dl>
|
|
98
|
+
</Section>
|
|
94
99
|
</template>
|
|
95
100
|
|
|
96
101
|
<script setup>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
102
|
+
import { ref, onMounted } from 'vue'
|
|
103
|
+
import Section from '../components/Section.vue'
|
|
104
|
+
import Navigation from '../components/Navigation.vue'
|
|
105
|
+
import NavigationGrid from '../components/NavigationGrid.vue'
|
|
106
|
+
import { useNotificationPopups } from '../composables/useNotificationPopups.js'
|
|
107
|
+
import logoUrl from '/logo.png'
|
|
108
|
+
|
|
109
|
+
const { show: showPopup } = useNotificationPopups()
|
|
110
|
+
const welcomeNavigation = ref(null)
|
|
111
|
+
|
|
112
|
+
const popularExamples = [
|
|
113
|
+
{
|
|
114
|
+
name: 'TableExample',
|
|
115
|
+
category: 'Data display',
|
|
116
|
+
label: 'Table',
|
|
117
|
+
description: 'Sortable columns and pagination',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'CalendarExample',
|
|
121
|
+
category: 'Data display',
|
|
122
|
+
label: 'Calendar',
|
|
123
|
+
description: 'Events, drag-and-drop, and date ranges',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'ButtonsExample',
|
|
127
|
+
category: 'Forms & input',
|
|
128
|
+
label: 'Buttons',
|
|
129
|
+
description: 'Variants, toolbars, and form actions',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'StatusExample',
|
|
133
|
+
category: 'Feedback',
|
|
134
|
+
label: 'Status',
|
|
135
|
+
description: 'Inline messages and notification popups',
|
|
136
|
+
},
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
function showWelcomeToast() {
|
|
140
|
+
showPopup({
|
|
141
|
+
label: 'WELCOME',
|
|
142
|
+
class: 'success',
|
|
143
|
+
message: 'Thanks for exploring PicoCrank!',
|
|
144
|
+
linkTo: { name: 'TableExample' },
|
|
145
|
+
linkLabel: 'Try the table example',
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
onMounted(() => {
|
|
150
|
+
if (!welcomeNavigation.value) {
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const nav = welcomeNavigation.value
|
|
155
|
+
|
|
156
|
+
nav.addRouterLink('PageStructure', 'Page structure', {
|
|
157
|
+
description: 'Sidebar, header, QuickSearch, and breadcrumbs',
|
|
158
|
+
})
|
|
159
|
+
nav.addRouterLink('TableExample', 'Table', {
|
|
160
|
+
description: 'Lists with sorting and pagination',
|
|
161
|
+
})
|
|
162
|
+
nav.addRouterLink('CalendarExample', 'Calendar', {
|
|
163
|
+
description: 'Month view with events',
|
|
164
|
+
})
|
|
165
|
+
nav.addRouterLink('ButtonsExample', 'Buttons', {
|
|
166
|
+
description: 'Button variants and toolbars',
|
|
167
|
+
})
|
|
168
|
+
nav.addRouterLink('FormExample', 'Forms', {
|
|
169
|
+
description: 'Labels, inputs, and fieldsets',
|
|
170
|
+
})
|
|
171
|
+
nav.addRouterLink('LoginExample', 'Login', {
|
|
172
|
+
description: 'Auth form with OAuth tabs',
|
|
173
|
+
})
|
|
174
|
+
nav.addRouterLink('StatusExample', 'Status', {
|
|
175
|
+
description: 'Notifications and inline status',
|
|
176
|
+
})
|
|
177
|
+
nav.addRouterLink('NavigationGridExample', 'Navigation grid', {
|
|
178
|
+
description: 'Icon grid for settings hubs',
|
|
179
|
+
})
|
|
180
|
+
nav.addRouterLink('TabsExample', 'Tabs', {
|
|
181
|
+
description: 'Tabbed content regions',
|
|
182
|
+
})
|
|
183
|
+
})
|
|
114
184
|
</script>
|
|
185
|
+
|
|
186
|
+
<style scoped>
|
|
187
|
+
.welcome-hero-title {
|
|
188
|
+
display: flex;
|
|
189
|
+
align-items: center;
|
|
190
|
+
gap: 0.75rem;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.welcome-logo {
|
|
194
|
+
width: 2.5rem;
|
|
195
|
+
height: 2.5rem;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.welcome-lead {
|
|
199
|
+
font-size: 1.05rem;
|
|
200
|
+
max-width: 42rem;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.welcome-actions {
|
|
204
|
+
display: flex;
|
|
205
|
+
flex-wrap: wrap;
|
|
206
|
+
gap: 0.5rem;
|
|
207
|
+
margin-bottom: 0.75rem;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.welcome-popular-grid {
|
|
211
|
+
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.welcome-popular-card {
|
|
215
|
+
display: flex;
|
|
216
|
+
flex-direction: column;
|
|
217
|
+
gap: 0.35rem;
|
|
218
|
+
text-decoration: none;
|
|
219
|
+
color: inherit;
|
|
220
|
+
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.welcome-popular-card:hover {
|
|
224
|
+
transform: translateY(-2px);
|
|
225
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.welcome-popular-description {
|
|
229
|
+
font-size: 0.85rem;
|
|
230
|
+
color: #666;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
html[data-theme="dark"] .welcome-popular-description {
|
|
234
|
+
color: #bbb;
|
|
235
|
+
}
|
|
236
|
+
</style>
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<Section title="User Control Panel" subtitle="Manage your account settings and preferences">
|
|
3
|
-
<Navigation ref="localNavigation">
|
|
4
|
-
<NavigationGrid />
|
|
5
|
-
</Navigation>
|
|
6
|
-
</Section>
|
|
7
|
-
</template>
|
|
8
|
-
|
|
9
|
-
<script setup>
|
|
10
|
-
import { ref, onMounted } from 'vue';
|
|
11
|
-
import Section from '../components/Section.vue';
|
|
12
|
-
import NavigationGrid from '../components/NavigationGrid.vue';
|
|
13
|
-
import Navigation from '../components/Navigation.vue';
|
|
14
|
-
import {
|
|
15
|
-
ViewIcon,
|
|
16
|
-
EditIcon,
|
|
17
|
-
SecurityValidationIcon,
|
|
18
|
-
HomeIcon
|
|
19
|
-
} from '@hugeicons/core-free-icons';
|
|
20
|
-
|
|
21
|
-
const localNavigation = ref(null);
|
|
22
|
-
|
|
23
|
-
onMounted(() => {
|
|
24
|
-
if (localNavigation.value) {
|
|
25
|
-
// Add user control panel links to local navigation
|
|
26
|
-
localNavigation.value.addCallback('Profile', () => {
|
|
27
|
-
alert('Navigate to Profile');
|
|
28
|
-
}, {
|
|
29
|
-
icon: ViewIcon,
|
|
30
|
-
name: 'user-profile',
|
|
31
|
-
description: 'View and edit your profile information'
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
localNavigation.value.addCallback('Account Settings', () => {
|
|
35
|
-
alert('Navigate to Account Settings');
|
|
36
|
-
}, {
|
|
37
|
-
icon: EditIcon,
|
|
38
|
-
name: 'account-settings',
|
|
39
|
-
description: 'Manage your account configuration'
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
localNavigation.value.addCallback('Security', () => {
|
|
43
|
-
alert('Navigate to Security Settings');
|
|
44
|
-
}, {
|
|
45
|
-
icon: SecurityValidationIcon,
|
|
46
|
-
name: 'security-settings',
|
|
47
|
-
description: 'Change password and security options'
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
localNavigation.value.addCallback('Preferences', () => {
|
|
51
|
-
alert('Navigate to Preferences');
|
|
52
|
-
}, {
|
|
53
|
-
icon: HomeIcon,
|
|
54
|
-
name: 'preferences',
|
|
55
|
-
description: 'Customize your application preferences'
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
localNavigation.value.addCallback('Notifications', () => {
|
|
59
|
-
alert('Navigate to Notifications');
|
|
60
|
-
}, {
|
|
61
|
-
icon: ViewIcon,
|
|
62
|
-
name: 'notifications',
|
|
63
|
-
description: 'Manage notification settings',
|
|
64
|
-
iconColor: '#e67e22',
|
|
65
|
-
indicator: true
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
localNavigation.value.addCallback('Billing', () => {
|
|
69
|
-
alert('Navigate to Billing');
|
|
70
|
-
}, {
|
|
71
|
-
icon: EditIcon,
|
|
72
|
-
name: 'billing',
|
|
73
|
-
description: 'View billing and subscription details',
|
|
74
|
-
disabled: true
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
</script>
|
|
79
|
-
|
|
80
|
-
<style scoped>
|
|
81
|
-
/* Additional styles if needed */
|
|
82
|
-
</style>
|