Compare commits

...

2 Commits

Author SHA1 Message Date
brian b08d347727 adding a profile page 2026-06-01 16:34:07 -06:00
brian 1d00b8e171 got a basic dashboard going 2026-06-01 16:30:02 -06:00
15 changed files with 998 additions and 58 deletions
+3 -3
View File
@@ -39,9 +39,9 @@
<!-- <link href="https://example.com/favicon.png" rel="icon" sizes="192x192"> -->
<!-- Font preloads (should be done for each font file) -->
<link href="public/fonts/material-icons-round/material-icons-round-latin-400-normal.woff2" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
<link href="public/fonts/roboto/Roboto-VariableFont_wdth-wght.woff2" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
<link href="public/fonts/roboto-slab/RobotoSlab-VariableFont_wght.woff2" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
<link href="/fonts/material-icons-round/material-icons-round-latin-400-normal.woff2" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
<link href="/fonts/roboto/Roboto-VariableFont_wdth-wght.woff2" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
<link href="/fonts/roboto-slab/RobotoSlab-VariableFont_wght.woff2" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
<!-- CSS -->
<link href="/src/style.css" rel="stylesheet" media="screen">
+2 -1
View File
@@ -9,7 +9,8 @@
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.5.0"
"vue": "^3.5.0",
"vue-router": "^4.4.0"
},
"devDependencies": {
"@tailwindcss/vite": "^4.0.0",
+18
View File
@@ -11,6 +11,9 @@ importers:
vue:
specifier: ^3.5.0
version: 3.5.35
vue-router:
specifier: ^4.4.0
version: 4.6.4(vue@3.5.35)
devDependencies:
'@tailwindcss/vite':
specifier: ^4.0.0
@@ -470,6 +473,9 @@ packages:
'@vue/compiler-ssr@3.5.35':
resolution: {integrity: sha512-rGhAeXgdM7/ffTJGXT69rCCdTmjDewnFuUZfBQQHTdcEBeWdT5HCGY60y2ytLJr9/Dsu7IntUi5z/w0h6Rjnzw==}
'@vue/devtools-api@6.6.4':
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
'@vue/reactivity@3.5.35':
resolution: {integrity: sha512-tVc+SsHConvh/Lz64qq1pP3rYArBmK42xonovEcxY74SQtvctZodG/zhq54P5dr38cVuw25d27cPNRdlMidpGQ==}
@@ -684,6 +690,11 @@ packages:
yaml:
optional: true
vue-router@4.6.4:
resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==}
peerDependencies:
vue: ^3.5.0
vue@3.5.35:
resolution: {integrity: sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q==}
peerDependencies:
@@ -984,6 +995,8 @@ snapshots:
'@vue/compiler-dom': 3.5.35
'@vue/shared': 3.5.35
'@vue/devtools-api@6.6.4': {}
'@vue/reactivity@3.5.35':
dependencies:
'@vue/shared': 3.5.35
@@ -1181,6 +1194,11 @@ snapshots:
jiti: 2.7.0
lightningcss: 1.32.0
vue-router@4.6.4(vue@3.5.35):
dependencies:
'@vue/devtools-api': 6.6.4
vue: 3.5.35
vue@3.5.35:
dependencies:
'@vue/compiler-dom': 3.5.35
+1 -53
View File
@@ -1,55 +1,3 @@
<script setup>
</script>
<template>
<div class="min-h-screen bg-light font-sans text-dark antialiased">
<div class="max-w-7xl mx-auto px-8 py-16">
<h1 class="text-3xl font-bold mb-2">MK Design System</h1>
<p class="text-secondary">Phase 1 &amp; 2 complete components coming in Phase 3+</p>
<div class="mt-12 grid gap-4 grid-cols-2 lg:grid-cols-4">
<div class="rounded-xl p-6 bg-gradient-primary text-white shadow-primary">
<p class="text-sm font-medium opacity-80">Primary gradient</p>
<p class="text-lg font-bold mt-1">#e91e63</p>
</div>
<div class="rounded-xl p-6 bg-gradient-secondary text-white shadow-secondary">
<p class="text-sm font-medium opacity-80">Secondary gradient</p>
<p class="text-lg font-bold mt-1">#7b809a</p>
</div>
<div class="rounded-xl p-6 bg-gradient-success text-white shadow-success">
<p class="text-sm font-medium opacity-80">Success gradient</p>
<p class="text-lg font-bold mt-1">#4caf50</p>
</div>
<div class="rounded-xl p-6 bg-gradient-info text-white shadow-info">
<p class="text-sm font-medium opacity-80">Info gradient</p>
<p class="text-lg font-bold mt-1">#1a73e8</p>
</div>
<div class="rounded-xl p-6 bg-gradient-warning text-white shadow-warning">
<p class="text-sm font-medium opacity-80">Warning gradient</p>
<p class="text-lg font-bold mt-1">#fb8c00</p>
</div>
<div class="rounded-xl p-6 bg-gradient-danger text-white shadow-danger">
<p class="text-sm font-medium opacity-80">Danger gradient</p>
<p class="text-lg font-bold mt-1">#f44335</p>
</div>
<div class="rounded-xl p-6 bg-gradient-dark text-white shadow-dark">
<p class="text-sm font-medium opacity-80">Dark gradient</p>
<p class="text-lg font-bold mt-1">#344767</p>
</div>
<div class="rounded-xl p-6 bg-gradient-light text-dark shadow-soft-md">
<p class="text-sm font-medium opacity-60">Light gradient</p>
<p class="text-lg font-bold mt-1">#f0f2f5</p>
</div>
</div>
<div class="mt-12 flex flex-wrap gap-3">
<span class="text-sm font-medium text-dark">Shadows:</span>
<span class="px-4 py-2 bg-white rounded-lg shadow-soft-xs text-sm">soft-xs</span>
<span class="px-4 py-2 bg-white rounded-lg shadow-soft-sm text-sm">soft-sm</span>
<span class="px-4 py-2 bg-white rounded-lg shadow-soft-md text-sm">soft-md</span>
<span class="px-4 py-2 bg-white rounded-lg shadow-soft-lg text-sm">soft-lg</span>
<span class="px-4 py-2 bg-white rounded-lg shadow-blur text-sm">blur</span>
</div>
</div>
</div>
<RouterView />
</template>
+134
View File
@@ -0,0 +1,134 @@
<script setup>
import { ref, computed } from 'vue'
import { useRoute, RouterLink } from 'vue-router'
const route = useRoute()
const sidebarOpen = ref(false)
const navItems = [
{ label: 'Dashboard', path: '/', icon: 'dashboard' },
{ label: 'Table List', path: '/tables', icon: 'table_view' },
{ label: 'Typography', path: '/typography', icon: 'text_fields' },
{ label: 'Icons', path: '/icons', icon: 'auto_awesome' },
{ label: 'Maps', path: '/maps', icon: 'map' },
{ label: 'Notifications', path: '/notifications',icon: 'notifications' },
]
function isActive(path) {
if (path === '/') return route.path === '/'
return route.path.startsWith(path)
}
const pageTitle = computed(() => route.meta.title ?? '')
</script>
<template>
<!-- Sidebar backdrop on mobile -->
<div
v-if="sidebarOpen"
class="fixed inset-0 z-20 bg-black/40 lg:hidden"
@click="sidebarOpen = false"
/>
<!-- Sidebar -->
<aside
class="fixed inset-y-0 left-0 z-30 flex w-64 flex-col bg-gradient-dark shadow-dark transition-transform duration-300"
:class="sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'"
>
<!-- Brand -->
<div class="flex items-center gap-3 px-6 py-5">
<div class="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gradient-primary shadow-primary">
<span class="material-icons text-base text-white">layers</span>
</div>
<span class="text-sm font-semibold text-white">MK Design System</span>
</div>
<div class="mx-4 border-t border-white/20" />
<!-- Nav -->
<nav class="flex-1 space-y-0.5 overflow-y-auto px-4 py-4">
<RouterLink
v-for="item in navItems"
:key="item.path"
:to="item.path"
class="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-200"
:class="isActive(item.path)
? 'bg-gradient-primary shadow-primary text-white'
: 'text-white/60 hover:bg-white/10 hover:text-white'"
@click="sidebarOpen = false"
>
<span class="material-icons text-lg leading-none">{{ item.icon }}</span>
{{ item.label }}
</RouterLink>
</nav>
<!-- Footer -->
<div class="mx-4 border-t border-white/20" />
<div class="space-y-0.5 px-4 py-4">
<RouterLink
to="/profile"
class="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-200"
:class="isActive('/profile')
? 'bg-gradient-primary shadow-primary text-white'
: 'text-white/60 hover:bg-white/10 hover:text-white'"
@click="sidebarOpen = false"
>
<span class="material-icons text-lg leading-none">person</span>
Profile
</RouterLink>
<a
href="#"
class="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium text-white/60 transition-all hover:bg-white/10 hover:text-white"
>
<span class="material-icons text-lg leading-none">settings</span>
Settings
</a>
<a
href="#"
class="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium text-white/60 transition-all hover:bg-white/10 hover:text-white"
>
<span class="material-icons text-lg leading-none">logout</span>
Log Out
</a>
</div>
</aside>
<!-- Main -->
<div class="flex min-h-screen flex-col lg:pl-64">
<!-- Top navbar -->
<header class="sticky top-0 z-20 flex items-center justify-between bg-white/80 px-6 py-3 shadow-soft-sm backdrop-blur-sm">
<div class="flex items-center gap-4">
<!-- Hamburger (mobile) -->
<button
class="rounded-lg p-1.5 text-secondary hover:bg-gray-100 lg:hidden"
@click="sidebarOpen = true"
>
<span class="material-icons text-xl leading-none">menu</span>
</button>
<!-- Breadcrumb -->
<div>
<p class="text-xs text-secondary">Pages / {{ pageTitle }}</p>
<h6 class="text-sm font-bold text-dark">{{ pageTitle }}</h6>
</div>
</div>
<!-- Right actions -->
<div class="flex items-center gap-2">
<button class="rounded-lg p-2 text-secondary hover:bg-gray-100 hover:text-dark">
<span class="material-icons text-xl leading-none">notifications</span>
</button>
<button class="rounded-lg p-2 text-secondary hover:bg-gray-100 hover:text-dark">
<span class="material-icons text-xl leading-none">account_circle</span>
</button>
</div>
</header>
<!-- Page content -->
<main class="flex-1 px-6 py-6">
<slot />
</main>
</div>
</template>
+2 -1
View File
@@ -1,5 +1,6 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/index.js'
createApp(App).mount('#app')
createApp(App).use(router).mount('#app')
+15
View File
@@ -0,0 +1,15 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
export default createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: HomeView, meta: { title: 'Dashboard' } },
{ path: '/tables', component: () => import('../views/TableListView.vue'), meta: { title: 'Table List' } },
{ path: '/typography', component: () => import('../views/TypographyView.vue'), meta: { title: 'Typography' } },
{ path: '/icons', component: () => import('../views/IconsView.vue'), meta: { title: 'Icons' } },
{ path: '/maps', component: () => import('../views/MapsView.vue'), meta: { title: 'Maps' } },
{ path: '/notifications',component: () => import('../views/NotificationsView.vue'), meta: { title: 'Notifications' } },
{ path: '/profile', component: () => import('../views/ProfileView.vue'), meta: { title: 'Profile' } },
],
})
+9
View File
@@ -42,6 +42,15 @@
@import "tailwindcss";
/* ============================================================
SAFELIST
Utilities built from dynamic class strings (template literals)
are invisible to content scanning. @source inline() forces
Tailwind to generate them regardless.
============================================================ */
@source inline("bg-gradient-primary bg-gradient-secondary bg-gradient-success bg-gradient-warning bg-gradient-danger bg-gradient-info bg-gradient-dark bg-gradient-light shadow-primary shadow-secondary shadow-success shadow-warning shadow-danger shadow-info shadow-dark");
/* ============================================================
DESIGN TOKENS
All values exposed as CSS custom properties AND Tailwind
+111
View File
@@ -0,0 +1,111 @@
<script setup>
import DashboardLayout from '../layouts/DashboardLayout.vue'
const stats = [
{ label: "Today's Money", value: '$53,000', change: '+55%', positive: true, icon: 'weekend', gradient: 'primary' },
{ label: "Today's Users", value: '2,300', change: '+3%', positive: true, icon: 'leaderboard',gradient: 'info' },
{ label: 'New Clients', value: '+3,462', change: '-2%', positive: false,icon: 'store', gradient: 'success' },
{ label: 'Sales', value: '$103,430',change: '+5%', positive: true, icon: 'person', gradient: 'danger' },
]
const projects = [
{ name: 'Material UI XD', budget: '$14,000', progress: 60, status: 'working' },
{ name: 'Add Progress Track', budget: '$3,000', progress: 10, status: 'cancelled' },
{ name: 'Fix Platform Errors',budget: 'Not set', progress: 100, status: 'done' },
{ name: 'Launch Mobile App', budget: '$20,500', progress: 100, status: 'done' },
{ name: 'Add the New Pricing',budget: '$500', progress: 25, status: 'working' },
{ name: 'Redesign New Online',budget: '$2,000', progress: 40, status: 'cancelled' },
]
const statusClass = {
done: 'bg-success/10 text-success',
working: 'bg-info/10 text-info',
cancelled: 'bg-danger/10 text-danger',
}
const gradientProgress = (value, gradient) => ({
width: `${value}%`,
})
</script>
<template>
<DashboardLayout>
<!-- Stat cards -->
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 xl:grid-cols-4 mt-6">
<div
v-for="stat in stats"
:key="stat.label"
class="rounded-2xl bg-white p-4 shadow-soft-md"
>
<div class="flex items-start justify-between">
<div
class="-mt-10 flex h-16 w-16 shrink-0 items-center justify-center rounded-xl shadow-soft-md"
:class="`bg-gradient-${stat.gradient} shadow-${stat.gradient}`"
>
<span class="material-icons text-3xl text-white">{{ stat.icon }}</span>
</div>
<div class="text-right">
<p class="text-sm font-medium text-secondary">{{ stat.label }}</p>
<h4 class="text-2xl font-bold text-dark">{{ stat.value }}</h4>
</div>
</div>
<div class="mt-4 border-t border-gray-100 pt-3">
<p class="text-sm text-secondary">
<span :class="stat.positive ? 'text-success' : 'text-danger'" class="font-medium">
{{ stat.change }}
</span>
than last week
</p>
</div>
</div>
</div>
<!-- Projects table -->
<div class="mt-6 rounded-2xl bg-white shadow-soft-md">
<div class="flex items-center justify-between border-b border-gray-100 px-6 py-4">
<h6 class="text-sm font-bold text-dark">Projects</h6>
<p class="text-xs text-secondary">
<span class="font-medium text-success">30 done</span> this month
</p>
</div>
<div class="overflow-x-auto">
<table class="w-full text-left text-sm">
<thead>
<tr class="border-b border-gray-100">
<th class="px-6 py-3 text-xs font-bold uppercase tracking-wide text-secondary">Project</th>
<th class="px-6 py-3 text-xs font-bold uppercase tracking-wide text-secondary">Budget</th>
<th class="px-6 py-3 text-xs font-bold uppercase tracking-wide text-secondary">Status</th>
<th class="px-6 py-3 text-xs font-bold uppercase tracking-wide text-secondary">Completion</th>
</tr>
</thead>
<tbody>
<tr
v-for="(p, i) in projects"
:key="p.name"
:class="i < projects.length - 1 ? 'border-b border-gray-100' : ''"
>
<td class="px-6 py-3 font-medium text-dark">{{ p.name }}</td>
<td class="px-6 py-3 text-secondary">{{ p.budget }}</td>
<td class="px-6 py-3">
<span class="rounded-full px-2.5 py-1 text-xs font-medium capitalize" :class="statusClass[p.status]">
{{ p.status }}
</span>
</td>
<td class="px-6 py-3">
<div class="flex items-center gap-3">
<div class="h-1.5 flex-1 overflow-hidden rounded-full bg-gray-200">
<div
class="h-full rounded-full bg-gradient-info"
:style="{ width: `${p.progress}%` }"
/>
</div>
<span class="w-8 shrink-0 text-right text-xs font-medium text-dark">{{ p.progress }}%</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</DashboardLayout>
</template>
+65
View File
@@ -0,0 +1,65 @@
<script setup>
import DashboardLayout from '../layouts/DashboardLayout.vue'
const groups = [
{
label: 'Navigation',
icons: ['home','menu','close','arrow_back','arrow_forward','chevron_left','chevron_right',
'expand_more','expand_less','more_vert','more_horiz','open_in_new','launch'],
},
{
label: 'Actions',
icons: ['add','edit','delete','save','search','filter_list','sort','share','download',
'upload','copy_all','content_paste','refresh','sync'],
},
{
label: 'Status & Feedback',
icons: ['check_circle','error','warning','info','notifications','notification_important',
'done','done_all','pending','hourglass_empty','lock','lock_open'],
},
{
label: 'Content',
icons: ['dashboard','table_view','text_fields','auto_awesome','map','bar_chart',
'pie_chart','analytics','insights','summarize','article','description'],
},
{
label: 'People & Social',
icons: ['person','group','account_circle','face','supervised_user_circle',
'person_add','logout','login','badge','contacts'],
},
{
label: 'Media & Files',
icons: ['image','photo','video_library','music_note','folder','folder_open',
'attachment','cloud','cloud_upload','cloud_download','storage','backup'],
},
]
</script>
<template>
<DashboardLayout>
<div class="space-y-6">
<div
v-for="group in groups"
:key="group.label"
class="rounded-2xl bg-white p-6 shadow-soft-md"
>
<h6 class="mb-4 text-sm font-bold text-dark">{{ group.label }}</h6>
<div class="grid grid-cols-4 gap-3 sm:grid-cols-6 md:grid-cols-8 lg:grid-cols-10 xl:grid-cols-13">
<div
v-for="icon in group.icons"
:key="icon"
class="group flex flex-col items-center gap-2 rounded-xl p-3 transition-all hover:bg-primary/5 hover:shadow-soft-xs cursor-default"
:title="icon"
>
<span class="material-icons text-2xl text-secondary transition-colors group-hover:text-primary">
{{ icon }}
</span>
<span class="w-full truncate text-center text-xs text-secondary group-hover:text-dark">
{{ icon }}
</span>
</div>
</div>
</div>
</div>
</DashboardLayout>
</template>
+87
View File
@@ -0,0 +1,87 @@
<script setup>
import DashboardLayout from '../layouts/DashboardLayout.vue'
const integrations = [
{
name: 'Google Maps',
package: '@googlemaps/js-api-loader',
description: 'Full-featured maps with Places, Directions, and Street View. Requires an API key.',
docs: 'https://developers.google.com/maps/documentation/javascript',
},
{
name: 'Mapbox GL JS',
package: 'mapbox-gl',
description: 'High-performance vector tile maps with rich customization and offline support.',
docs: 'https://docs.mapbox.com/mapbox-gl-js/',
},
{
name: 'Leaflet + vue-leaflet',
package: '@vue-leaflet/vue-leaflet',
description: 'Lightweight, open-source maps using OpenStreetMap tiles. No API key required.',
docs: 'https://vue-leaflet.github.io/vue-leaflet/',
},
]
</script>
<template>
<DashboardLayout>
<div class="space-y-6">
<!-- Placeholder map area -->
<div class="relative overflow-hidden rounded-2xl bg-white shadow-soft-md">
<div class="flex h-96 items-center justify-center bg-gradient-to-br from-gray-100 to-gray-200">
<!-- Grid lines to suggest a map -->
<svg class="absolute inset-0 h-full w-full opacity-30" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#adb5bd" stroke-width="0.5"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" />
</svg>
<!-- Simulated pin -->
<div class="relative flex flex-col items-center">
<div class="flex h-14 w-14 items-center justify-center rounded-full bg-gradient-primary shadow-primary">
<span class="material-icons text-2xl text-white">place</span>
</div>
<div class="-mt-1 h-3 w-px bg-primary" />
<div class="mt-4 rounded-xl bg-white px-6 py-4 shadow-soft-lg text-center">
<p class="text-sm font-bold text-dark">Map Integration</p>
<p class="text-xs text-secondary">Mount your map library component here</p>
</div>
</div>
</div>
</div>
<!-- Integration options -->
<div class="rounded-2xl bg-white p-6 shadow-soft-md">
<h6 class="mb-1 text-sm font-bold text-dark">Recommended Integrations</h6>
<p class="mb-6 text-xs text-secondary">
Maps require a third-party library. Choose one and mount it inside
<code class="rounded bg-gray-100 px-1.5 py-0.5 font-mono text-xs text-primary">MapsView.vue</code>.
</p>
<div class="grid gap-4 sm:grid-cols-3">
<div
v-for="lib in integrations"
:key="lib.name"
class="rounded-xl border border-gray-200 p-4 transition-shadow hover:shadow-soft-sm"
>
<p class="font-semibold text-dark text-sm">{{ lib.name }}</p>
<code class="mt-1 block text-xs text-secondary font-mono">{{ lib.package }}</code>
<p class="mt-2 text-xs text-secondary leading-relaxed">{{ lib.description }}</p>
<a
:href="lib.docs"
target="_blank"
rel="noopener"
class="mt-3 inline-flex items-center gap-1 text-xs font-medium text-primary hover:underline"
>
Docs <span class="material-icons text-xs leading-none">open_in_new</span>
</a>
</div>
</div>
</div>
</div>
</DashboardLayout>
</template>
+110
View File
@@ -0,0 +1,110 @@
<script setup>
import { ref } from 'vue'
import DashboardLayout from '../layouts/DashboardLayout.vue'
const dismissed = ref(new Set())
const alerts = [
{ id: 1, type: 'success', icon: 'check_circle', title: 'Success', message: 'This is a success notification — something went well.' },
{ id: 2, type: 'info', icon: 'info', title: 'Info', message: 'This is an info notification — just letting you know.' },
{ id: 3, type: 'warning', icon: 'warning', title: 'Warning', message: 'This is a warning notification — you should take action.' },
{ id: 4, type: 'danger', icon: 'error', title: 'Error', message: 'This is an error notification — something went wrong.' },
]
const typeStyle = {
success: 'bg-success text-white',
info: 'bg-info text-white',
warning: 'bg-warning text-white',
danger: 'bg-danger text-white',
}
const notifications = [
{ id: 10, avatar: 'PC', color: 'primary', name: 'Patrick Collins', time: '13 minutes ago', text: 'New message in Design System channel' },
{ id: 11, avatar: 'MR', color: 'info', name: 'Mikaela Ramos', time: '2 hours ago', text: 'Updated the icon grid component' },
{ id: 12, avatar: 'TP', color: 'success', name: 'Tom Perez', time: 'Yesterday', text: 'Released v0.2.0 with table improvements' },
{ id: 13, avatar: 'LC', color: 'warning', name: 'Lacina Cosmo', time: '2 days ago', text: 'Flagged a responsiveness issue on mobile' },
{ id: 14, avatar: 'AM', color: 'danger', name: 'Ally Maria', time: 'Last week', text: 'Added font preload entries to index.html' },
]
function dismiss(id) {
dismissed.value.add(id)
}
</script>
<template>
<DashboardLayout>
<div class="grid gap-6 lg:grid-cols-2">
<!-- Dismissible alerts -->
<div class="rounded-2xl bg-white p-6 shadow-soft-md">
<h6 class="mb-1 text-sm font-bold text-dark">Alert States</h6>
<p class="mb-6 text-xs text-secondary">Dismissible alerts for each semantic color</p>
<div class="space-y-3">
<transition-group name="alert">
<div
v-for="alert in alerts"
v-show="!dismissed.has(alert.id)"
:key="alert.id"
class="flex items-start gap-3 rounded-lg p-4 text-sm font-medium"
:class="typeStyle[alert.type]"
>
<span class="material-icons text-lg leading-none shrink-0">{{ alert.icon }}</span>
<p class="flex-1">
<span class="font-bold">{{ alert.title }} </span>{{ alert.message }}
</p>
<button
class="shrink-0 opacity-70 hover:opacity-100 transition-opacity"
@click="dismiss(alert.id)"
>
<span class="material-icons text-lg leading-none">close</span>
</button>
</div>
</transition-group>
</div>
</div>
<!-- Notification feed -->
<div class="rounded-2xl bg-white p-6 shadow-soft-md">
<div class="mb-6 flex items-center justify-between">
<h6 class="text-sm font-bold text-dark">Recent Notifications</h6>
<span class="rounded-full bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary">
{{ notifications.length }} new
</span>
</div>
<div class="space-y-4">
<div
v-for="n in notifications"
:key="n.id"
class="flex items-start gap-3"
>
<div
class="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl text-xs font-bold text-white shadow-soft-sm"
:class="`bg-gradient-${n.color} shadow-${n.color}`"
>
{{ n.avatar }}
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-dark truncate">{{ n.name }}</p>
<p class="text-xs text-secondary mt-0.5">{{ n.text }}</p>
</div>
<span class="shrink-0 text-xs text-secondary">{{ n.time }}</span>
</div>
</div>
</div>
</div>
</DashboardLayout>
</template>
<style scoped>
.alert-leave-active {
transition: all 0.3s ease;
}
.alert-leave-to {
opacity: 0;
transform: translateX(1rem);
max-height: 0;
margin: 0;
padding: 0;
}
</style>
+240
View File
@@ -0,0 +1,240 @@
<script setup>
import DashboardLayout from '../layouts/DashboardLayout.vue'
const stats = [
{ label: 'Followers', value: '4,812' },
{ label: 'Following', value: '294' },
{ label: 'Projects', value: '32' },
]
const socials = [
{ icon: 'facebook', color: 'text-blue-600', label: 'Facebook' },
{ icon: 'twitter', color: 'text-sky-500', label: 'Twitter' },
{ icon: 'instagram', color: 'text-pink-500', label: 'Instagram' },
]
const conversations = [
{ initials: 'OP', color: 'primary', name: 'Olivia Park', preview: 'Hey, I loved the new component library!', time: '5m' },
{ initials: 'JD', color: 'info', name: 'James Donovan', preview: 'Can you review my PR when you get a chance?', time: '22m' },
{ initials: 'SR', color: 'success', name: 'Sofia Reyes', preview: 'The design tokens look great in production.', time: '1h' },
{ initials: 'ML', color: 'warning', name: 'Marco Lin', preview: 'Pushed the icon fix — all shadows resolved.', time: '3h' },
{ initials: 'AM', color: 'secondary', name: 'Ally Maria', preview: 'Updated the font preload entries in index.html.', time: '1d' },
]
const projects = [
{ name: 'MK Design System', role: 'Lead', gradient: 'primary', members: ['PC', 'JD', 'SR'] },
{ name: 'Dashboard Rework', role: 'Contributor', gradient: 'info', members: ['ML', 'AM'] },
{ name: 'Icon Library', role: 'Owner', gradient: 'success', members: ['OP', 'JD'] },
{ name: 'Typography Scale', role: 'Reviewer', gradient: 'warning', members: ['SR', 'ML', 'PC'] },
]
const memberColor = { PC: 'primary', JD: 'info', SR: 'success', ML: 'warning', AM: 'secondary', OP: 'danger' }
</script>
<template>
<DashboardLayout>
<!-- Cover + avatar -->
<div class="relative mb-20">
<div class="h-48 overflow-hidden rounded-2xl bg-gradient-dark shadow-dark">
<!-- Decorative grid -->
<svg class="h-full w-full opacity-10" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="pgrid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="white" stroke-width="0.5"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#pgrid)" />
</svg>
</div>
<!-- Avatar -->
<div class="absolute -bottom-14 left-8 flex h-28 w-28 items-center justify-center rounded-2xl bg-gradient-primary text-4xl font-bold text-white shadow-primary ring-4 ring-white">
EC
</div>
</div>
<div class="grid gap-6 lg:grid-cols-3">
<!-- Left column -->
<div class="space-y-6">
<!-- Identity card -->
<div class="rounded-2xl bg-white p-6 shadow-soft-md">
<h4 class="text-xl font-bold text-dark">Esthera Carter</h4>
<p class="mt-0.5 text-sm text-secondary">Lead Designer · San Francisco, CA</p>
<!-- Stats -->
<div class="mt-5 grid grid-cols-3 divide-x divide-gray-100 rounded-xl border border-gray-100">
<div v-for="s in stats" :key="s.label" class="flex flex-col items-center py-3">
<span class="text-lg font-bold text-dark">{{ s.value }}</span>
<span class="text-xs text-secondary">{{ s.label }}</span>
</div>
</div>
<!-- Bio -->
<p class="mt-5 text-sm leading-relaxed text-secondary">
Building design systems and component libraries. Obsessed with typography,
spacing, and making sure every pixel is intentional.
</p>
<!-- Socials -->
<div class="mt-5 flex items-center gap-3">
<a
v-for="s in socials"
:key="s.icon"
href="#"
class="flex h-8 w-8 items-center justify-center rounded-lg bg-gray-100 transition-colors hover:bg-gray-200"
:title="s.label"
>
<span class="material-icons text-base" :class="s.color">tag</span>
</a>
</div>
</div>
<!-- Conversations -->
<div class="rounded-2xl bg-white p-6 shadow-soft-md">
<h6 class="mb-4 text-sm font-bold text-dark">Conversations</h6>
<div class="space-y-4">
<div
v-for="c in conversations"
:key="c.name"
class="flex items-start gap-3 cursor-pointer group"
>
<div
class="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl text-xs font-bold text-white"
:class="`bg-gradient-${c.color} shadow-${c.color}`"
>
{{ c.initials }}
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-dark group-hover:text-primary transition-colors">{{ c.name }}</p>
<p class="text-xs text-secondary truncate">{{ c.preview }}</p>
</div>
<span class="shrink-0 text-xs text-secondary">{{ c.time }}</span>
</div>
</div>
</div>
</div>
<!-- Right column -->
<div class="space-y-6 lg:col-span-2">
<!-- Projects -->
<div class="rounded-2xl bg-white p-6 shadow-soft-md">
<div class="mb-4 flex items-center justify-between">
<h6 class="text-sm font-bold text-dark">Projects</h6>
<button class="rounded-lg bg-gradient-primary px-4 py-2 text-xs font-medium text-white shadow-primary">
+ New
</button>
</div>
<div class="grid gap-4 sm:grid-cols-2">
<div
v-for="p in projects"
:key="p.name"
class="group relative overflow-hidden rounded-xl border border-gray-100 p-4 transition-shadow hover:shadow-soft-md"
>
<!-- Gradient accent bar -->
<div class="absolute left-0 top-0 h-full w-1 rounded-l-xl" :class="`bg-gradient-${p.gradient}`" />
<div class="pl-3">
<div class="flex items-start justify-between">
<div>
<p class="text-sm font-semibold text-dark">{{ p.name }}</p>
<span
class="mt-1 inline-block rounded-full px-2 py-0.5 text-xs font-medium"
:class="`bg-${p.gradient}/10 text-${p.gradient}`"
>
{{ p.role }}
</span>
</div>
<button class="text-secondary hover:text-dark">
<span class="material-icons text-base leading-none">more_vert</span>
</button>
</div>
<!-- Member avatars -->
<div class="mt-3 flex items-center">
<div class="flex -space-x-2">
<div
v-for="m in p.members"
:key="m"
class="flex h-7 w-7 items-center justify-center rounded-lg text-xs font-bold text-white ring-2 ring-white"
:class="`bg-gradient-${memberColor[m]} shadow-${memberColor[m]}`"
:title="m"
>
{{ m }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Settings -->
<div class="rounded-2xl bg-white p-6 shadow-soft-md">
<h6 class="mb-5 text-sm font-bold text-dark">Profile Settings</h6>
<div class="space-y-4">
<div class="grid gap-4 sm:grid-cols-2">
<div>
<label class="mb-1 block text-xs font-medium text-secondary">First Name</label>
<input
type="text"
value="Esthera"
class="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm text-dark outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors"
/>
</div>
<div>
<label class="mb-1 block text-xs font-medium text-secondary">Last Name</label>
<input
type="text"
value="Carter"
class="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm text-dark outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors"
/>
</div>
</div>
<div>
<label class="mb-1 block text-xs font-medium text-secondary">Email Address</label>
<input
type="email"
value="esthera@mkdesign.dev"
class="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm text-dark outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors"
/>
</div>
<div>
<label class="mb-1 block text-xs font-medium text-secondary">Location</label>
<input
type="text"
value="San Francisco, CA"
class="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm text-dark outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors"
/>
</div>
<div>
<label class="mb-1 block text-xs font-medium text-secondary">Bio</label>
<textarea
rows="3"
class="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm text-dark outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors resize-none"
>Building design systems and component libraries. Obsessed with typography, spacing, and making sure every pixel is intentional.</textarea>
</div>
<div class="flex justify-end">
<button class="rounded-lg bg-gradient-primary px-6 py-2.5 text-sm font-medium text-white shadow-primary hover:shadow-soft-md transition-shadow">
Save Changes
</button>
</div>
</div>
</div>
</div>
</div>
</DashboardLayout>
</template>
+92
View File
@@ -0,0 +1,92 @@
<script setup>
import DashboardLayout from '../layouts/DashboardLayout.vue'
const rows = [
{ initials: 'OA', color: 'primary', name: 'Esthera Jackson', email: 'esthera@argon.com', role: 'Manager', org: 'Organization', online: true, date: '23/04/18' },
{ initials: 'LB', color: 'info', name: 'Landon Brighton', email: 'landon@argon.com', role: 'Programator',org: 'Developer', online: false, date: '11/01/19' },
{ initials: 'MR', color: 'success', name: 'Mikaela Ramos', email: 'mikaela@argon.com', role: 'Executive', org: 'Projects', online: true, date: '19/09/17' },
{ initials: 'TP', color: 'warning', name: 'Tom Perez', email: 'tom@argon.com', role: 'Projects', org: 'Organization', online: true, date: '24/12/08' },
{ initials: 'LC', color: 'danger', name: 'Lacina Cosmo', email: 'lacina@argon.com', role: 'Programator',org: 'Developer', online: false, date: '04/10/21' },
{ initials: 'AM', color: 'secondary', name: 'Ally Maria', email: 'ally@argon.com', role: 'Manager', org: 'Executive', online: true, date: '14/03/20' },
]
</script>
<template>
<DashboardLayout>
<div class="rounded-2xl bg-white shadow-soft-md">
<!-- Header -->
<div class="flex items-center justify-between border-b border-gray-100 px-6 py-4">
<h6 class="text-sm font-bold text-dark">Authors Table</h6>
<button class="rounded-lg bg-gradient-primary px-4 py-2 text-xs font-medium text-white shadow-primary hover:shadow-soft-md transition-shadow">
+ New Entry
</button>
</div>
<!-- Table -->
<div class="overflow-x-auto">
<table class="w-full text-left text-sm">
<thead>
<tr class="border-b border-gray-100">
<th class="px-6 py-3 text-xs font-bold uppercase tracking-wide text-secondary">Author</th>
<th class="px-6 py-3 text-xs font-bold uppercase tracking-wide text-secondary">Function</th>
<th class="px-6 py-3 text-xs font-bold uppercase tracking-wide text-secondary">Status</th>
<th class="px-6 py-3 text-xs font-bold uppercase tracking-wide text-secondary">Employed</th>
<th class="px-6 py-3 text-xs font-bold uppercase tracking-wide text-secondary">Action</th>
</tr>
</thead>
<tbody>
<tr
v-for="(row, i) in rows"
:key="row.email"
:class="i < rows.length - 1 ? 'border-b border-gray-100' : ''"
>
<!-- Author -->
<td class="px-6 py-3">
<div class="flex items-center gap-3">
<div
class="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl text-xs font-bold text-white shadow-soft-sm"
:class="`bg-gradient-${row.color} shadow-${row.color}`"
>
{{ row.initials }}
</div>
<div>
<p class="font-medium text-dark">{{ row.name }}</p>
<p class="text-xs text-secondary">{{ row.email }}</p>
</div>
</div>
</td>
<!-- Function -->
<td class="px-6 py-3">
<p class="font-medium text-dark">{{ row.role }}</p>
<p class="text-xs text-secondary">{{ row.org }}</p>
</td>
<!-- Status -->
<td class="px-6 py-3">
<span
class="inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium"
:class="row.online ? 'bg-success/10 text-success' : 'bg-gray-100 text-secondary'"
>
<span class="h-1.5 w-1.5 rounded-full" :class="row.online ? 'bg-success' : 'bg-gray-400'" />
{{ row.online ? 'Online' : 'Offline' }}
</span>
</td>
<!-- Employed -->
<td class="px-6 py-3 text-secondary">{{ row.date }}</td>
<!-- Action -->
<td class="px-6 py-3">
<div class="flex items-center gap-2">
<button class="text-xs font-medium text-info hover:underline">Edit</button>
<button class="text-xs font-medium text-danger hover:underline">Delete</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</DashboardLayout>
</template>
+109
View File
@@ -0,0 +1,109 @@
<script setup>
import DashboardLayout from '../layouts/DashboardLayout.vue'
const headings = [
{ tag: 'h1', label: 'h1. Heading', size: 'text-5xl', weight: 'font-light' },
{ tag: 'h2', label: 'h2. Heading', size: 'text-4xl', weight: 'font-light' },
{ tag: 'h3', label: 'h3. Heading', size: 'text-3xl', weight: 'font-normal' },
{ tag: 'h4', label: 'h4. Heading', size: 'text-2xl', weight: 'font-normal' },
{ tag: 'h5', label: 'h5. Heading', size: 'text-xl', weight: 'font-medium' },
{ tag: 'h6', label: 'h6. Heading', size: 'text-base',weight: 'font-medium' },
]
const weights = [
{ label: 'Light 300', class: 'font-light', value: 300 },
{ label: 'Regular 400',class: 'font-normal', value: 400 },
{ label: 'Medium 500', class: 'font-medium', value: 500 },
{ label: 'Bold 700', class: 'font-bold', value: 700 },
{ label: 'Black 900', class: 'font-black', value: 900 },
]
const colors = [
{ label: 'Primary', class: 'text-primary' },
{ label: 'Secondary', class: 'text-secondary' },
{ label: 'Success', class: 'text-success' },
{ label: 'Warning', class: 'text-warning' },
{ label: 'Danger', class: 'text-danger' },
{ label: 'Info', class: 'text-info' },
{ label: 'Dark', class: 'text-dark' },
]
</script>
<template>
<DashboardLayout>
<div class="grid gap-6 lg:grid-cols-2">
<!-- Headings -->
<div class="rounded-2xl bg-white p-6 shadow-soft-md">
<h6 class="mb-1 text-sm font-bold text-dark">Headings</h6>
<p class="mb-6 text-xs text-secondary">Roboto Variable font-light through font-medium</p>
<div class="space-y-3">
<component
:is="h.tag"
v-for="h in headings"
:key="h.tag"
:class="[h.size, h.weight, 'text-dark leading-tight']"
>
{{ h.label }}
</component>
</div>
</div>
<!-- Font weights -->
<div class="rounded-2xl bg-white p-6 shadow-soft-md">
<h6 class="mb-1 text-sm font-bold text-dark">Font Weights</h6>
<p class="mb-6 text-xs text-secondary">Roboto Variable covers 100900 in a single file</p>
<div class="space-y-4">
<div v-for="w in weights" :key="w.value">
<p class="mb-0.5 text-xs text-secondary">{{ w.label }}</p>
<p class="text-xl text-dark" :class="w.class">
The quick brown fox jumps over the lazy dog
</p>
</div>
</div>
</div>
<!-- Body & UI text -->
<div class="rounded-2xl bg-white p-6 shadow-soft-md">
<h6 class="mb-1 text-sm font-bold text-dark">Body & UI Text</h6>
<p class="mb-6 text-xs text-secondary">Scale used across components</p>
<div class="space-y-4">
<div>
<p class="mb-1 text-xs text-secondary">text-base (1rem) body</p>
<p class="text-base text-dark">Design systems give teams a shared language for building consistent, scalable products.</p>
</div>
<div>
<p class="mb-1 text-xs text-secondary">text-sm (0.875rem) UI labels</p>
<p class="text-sm text-dark">Design systems give teams a shared language for building consistent, scalable products.</p>
</div>
<div>
<p class="mb-1 text-xs text-secondary">text-xs (0.75rem) captions, meta</p>
<p class="text-xs text-secondary">Design systems give teams a shared language for building consistent, scalable products.</p>
</div>
<div>
<p class="mb-1 text-xs text-secondary">Inline elements</p>
<p class="text-sm text-dark">
Regular text with <strong>bold emphasis</strong>, <em>italic style</em>,
and <code class="rounded bg-gray-100 px-1.5 py-0.5 font-mono text-xs text-primary">inline code</code> fragments.
</p>
</div>
</div>
</div>
<!-- Text colors -->
<div class="rounded-2xl bg-white p-6 shadow-soft-md">
<h6 class="mb-1 text-sm font-bold text-dark">Semantic Text Colors</h6>
<p class="mb-6 text-xs text-secondary">All map to @theme --color-* tokens</p>
<div class="space-y-3">
<div v-for="c in colors" :key="c.label" class="flex items-center gap-4">
<span class="w-20 shrink-0 text-xs text-secondary">{{ c.label }}</span>
<p class="text-sm font-medium" :class="c.class">
The quick brown fox jumps over the lazy dog
</p>
</div>
</div>
</div>
</div>
</DashboardLayout>
</template>