develop-updateui #1

Merged
lethdat merged 78 commits from develop-updateui into master 2026-04-02 05:59:23 +00:00
11 changed files with 106 additions and 59 deletions
Showing only changes of commit 4cc2cc0691 - Show all commits

2
components.d.ts vendored
View File

@@ -14,6 +14,7 @@ declare module 'vue' {
export interface GlobalComponents {
Add: typeof import('./src/components/icons/Add.vue')['default']
AlertTriangleIcon: typeof import('./src/components/icons/AlertTriangleIcon.vue')['default']
ArrowDownTray: typeof import('./src/components/icons/ArrowDownTray.vue')['default']
ArrowRightIcon: typeof import('./src/components/icons/ArrowRightIcon.vue')['default']
Bell: typeof import('./src/components/icons/Bell.vue')['default']
Button: typeof import('primevue/button')['default']
@@ -61,6 +62,7 @@ declare module 'vue' {
declare global {
const Add: typeof import('./src/components/icons/Add.vue')['default']
const AlertTriangleIcon: typeof import('./src/components/icons/AlertTriangleIcon.vue')['default']
const ArrowDownTray: typeof import('./src/components/icons/ArrowDownTray.vue')['default']
const ArrowRightIcon: typeof import('./src/components/icons/ArrowRightIcon.vue')['default']
const Bell: typeof import('./src/components/icons/Bell.vue')['default']
const Button: typeof import('primevue/button')['default']

View File

@@ -1,7 +1,6 @@
<script setup lang="ts">
import { cn } from '@/lib/utils';
import { VNode } from 'vue';
import VueHead from '@/components/VueHead';
interface Breadcrumb {
label: string;
@@ -16,7 +15,7 @@ interface Action {
}
interface Props {
title: string;
title: string | VNode;
description?: string;
breadcrumbs?: Breadcrumb[];
actions?: Action[];
@@ -67,8 +66,8 @@ const getButtonClass = (variant?: string) => {
<!-- Title & Actions -->
<div class="flex items-start justify-between gap-4 flex-wrap">
<div class="flex-1 min-w-0">
<h1 class="text-3xl font-bold text-gray-900 mb-1">{{ title }}</h1>
<vue-head :input="{ title, meta: [{ name: 'description', content: description || '' }] }" />
<h1 v-if="typeof props.title == 'string'" class="text-3xl font-bold text-gray-900 mb-1">{{ title }}</h1>
<component v-else :is="title" />
<p v-if="description" class="text-gray-600">{{ description }}</p>
</div>

View File

@@ -0,0 +1,17 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
v-if="!filled">
<path stroke-linecap="round" stroke-linejoin="round"
d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" v-else>
<path fill-rule="evenodd"
d="M12 2.25a.75.75 0 0 1 .75.75v11.69l3.22-3.22a.75.75 0 1 1 1.06 1.06l-4.5 4.5a.75.75 0 0 1-1.06 0l-4.5-4.5a.75.75 0 1 1 1.06-1.06l3.22 3.22V3a.75.75 0 0 1 .75-.75Zm-9 13.5a.75.75 0 0 1 .75.75v2.25a1.5 1.5 0 0 0 1.5 1.5h13.5a1.5 1.5 0 0 0 1.5-1.5V16.5a.75.75 0 0 1 1.5 0v2.25a3 3 0 0 1-3 3H5.25a3 3 0 0 1-3-3V16.5a.75.75 0 0 1 .75-.75Z"
clip-rule="evenodd" />
</svg>
</template>
<script lang="ts" setup>
defineProps<{
filled?: boolean
}>()
</script>

View File

@@ -1,13 +1,12 @@
<script setup lang="ts">
<script setup lang="tsx">
import { client, type ModelVideo } from '@/api/client';
import PageHeader from '@/components/dashboard/PageHeader.vue';
import { useAuthStore } from '@/stores/auth';
import { onMounted, ref } from 'vue';
import NameGradient from './components/NameGradient.vue';
import QuickActions from './components/QuickActions.vue';
import RecentVideos from './components/RecentVideos.vue';
import StatsOverview from './components/StatsOverview.vue';
const auth = useAuthStore()
const loading = ref(true);
const recentVideos = ref<ModelVideo[]>([]);
@@ -53,12 +52,12 @@ const fetchDashboardData = async () => {
onMounted(() => {
fetchDashboardData();
});
</script>
<template>
<div class="dashboard-overview">
<PageHeader :title="`Welcome back, ${auth.user?.username}! 👋`" description="Here's what's happening with your videos."
:breadcrumbs="[
<PageHeader :title="NameGradient" description="Welcome back, Here's what's happening with your videos." :breadcrumbs="[
{ label: 'Dashboard' }
]" />

View File

@@ -0,0 +1,10 @@
<template>
<div class="text-3xl font-bold text-gray-900 mb-1">
<span class=":uno: bg-[linear-gradient(130deg,#14a74b_0%,#22c55e_35%,#10b981_65%,#06b6d4_100%)] bg-clip-text text-transparent">Hello, {{ auth.user?.username }}</span>
</div>
</template>
<script setup lang="ts">
import { useAuthStore } from '@/stores/auth';
const auth = useAuthStore()
</script>

View File

@@ -29,19 +29,38 @@ const getStatusSeverity = (status: string) => {
return 'info';
}
};
import { useToast } from 'primevue/usetoast';
import ArrowDownTray from '@/components/icons/ArrowDownTray.vue';
const toast = useToast();
const downloadInvoice = (item: PaymentHistoryItem) => {
toast.add({
severity: 'info',
summary: 'Downloading',
detail: `Downloading invoice #${item.invoiceId}...`,
life: 2000
});
// Simulate download delay
setTimeout(() => {
toast.add({
severity: 'success',
summary: 'Downloaded',
detail: `Invoice #${item.invoiceId} downloaded successfully`,
life: 3000
});
}, 1500);
};
</script>
<template>
<section>
<h2 class="text-2xl font-bold mb-6 text-gray-900">Billing History</h2>
<div class="bg-white border border-gray-200 rounded-xl overflow-hidden shadow-sm">
<DataTable :value="history" tableStyle="min-width: 50rem"
:pt="{
thead: { class: 'bg-gray-50 border-b border-gray-200' },
headerRow: { class: 'text-gray-500 text-xs font-semibold uppercase tracking-wider' },
bodyRow: { class: 'text-gray-700 hover:bg-gray-50/50' }
}"
>
<div class="bg-white border border-gray-200 rounded-xl overflow-hidden">
<DataTable :value="history" responsiveLayout="scroll" class="w-full">
<template #empty>
<div class="text-center py-8 text-gray-500">No payment history found.</div>
</template>
@@ -54,19 +73,20 @@ const getStatusSeverity = (status: string) => {
<Column field="plan" header="Plan"></Column>
<Column field="status" header="Status">
<template #body="slotProps">
<Tag
:value="slotProps.data.status"
:severity="getStatusSeverity(slotProps.data.status)"
class="capitalize px-2 py-0.5 text-xs"
:rounded="true"
/>
<Tag :value="slotProps.data.status" :severity="getStatusSeverity(slotProps.data.status)"
class="capitalize px-2 py-0.5 text-xs" :rounded="true" />
</template>
</Column>
<Column header="" style="width: 3rem">
<template #body>
<Button icon="i-heroicons-arrow-down-tray" text rounded severity="secondary" size="small" />
<!-- <Column header="" style="width: 3rem">
<template #body="slotProps">
<Button text rounded severity="secondary" size="small" @click="downloadInvoice(slotProps.data)"
v-tooltip="'Download Invoice'">
<template #icon>
<ArrowDownTray class="w-5 h-5" />
</template>
</Column>
</Button>
</template>
</Column> -->
</DataTable>
</div>
</section>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { formatBytes } from '@/lib/utils';
import ProgressBar from 'primevue/progressbar';
import { computed } from 'vue';
import { formatBytes } from '@/lib/utils';
const props = defineProps<{
storageUsed: number;
@@ -15,7 +15,7 @@ const uploadsPercentage = computed(() => Math.min(Math.round((props.uploadsUsed
</script>
<template>
<div class="bg-white border border-gray-200 rounded-2xl p-8 shadow-sm flex flex-col justify-center">
<div class="bg-white border border-gray-200 rounded-2xl p-8 flex flex-col justify-center">
<h3 class="text-lg font-bold text-gray-900 mb-6">Usage Statistics</h3>
<div class="mb-6">

View File

@@ -21,7 +21,7 @@ const formatBytes = (bytes: number) => {
</script>
<template>
<div class="bg-white border border-gray-200 rounded-2xl p-6 shadow-sm">
<div class="bg-white border border-gray-200 rounded-2xl p-6">
<h3 class="text-lg font-bold text-gray-900 mb-4">Account Status</h3>
<div class="space-y-4">
<div>

View File

@@ -3,7 +3,7 @@ import Tag from 'primevue/tag';
</script>
<template>
<div class="bg-white border border-gray-200 rounded-2xl p-6 shadow-sm">
<div class="bg-white border border-gray-200 rounded-2xl p-6">
<h3 class="text-lg font-bold text-gray-900 mb-4">Linked Accounts</h3>
<div class="space-y-3">
<div class="flex items-center justify-between p-3 rounded-lg border border-gray-100 hover:border-gray-200 transition-colors">

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { ModelUser } from '@/api/client';
import InputText from 'primevue/inputtext';
import Button from 'primevue/button';
import InputText from 'primevue/inputtext';
defineProps<{
user: ModelUser | null;
@@ -14,7 +14,7 @@ const emit = defineEmits<{
</script>
<template>
<div class="bg-white border border-gray-200 rounded-2xl p-8 shadow-sm">
<div class="bg-white border border-gray-200 rounded-2xl p-8">
<div class="flex items-center justify-between mb-6">
<h3 class="text-xl font-bold text-gray-900">Personal Information</h3>
<div class="flex gap-2">

View File

@@ -45,18 +45,18 @@ export default defineConfig({
dark: "#14532d",
},
accent: {
DEFAULT: "#6366f1",
50: "#eef2ff",
100: "#e0e7ff",
200: "#c7d2fe",
300: "#a5b4fc",
400: "#818cf8",
500: "#6366f1",
600: "#4f46e5",
700: "#4338ca",
800: "#3730a3",
900: "#312e81",
950: "#1e1b4b",
DEFAULT: "#14a74b",
50: "#ecfdf3",
100: "#d1fae5",
200: "#a7f3d0",
300: "#6ee7b7",
400: "#34d399",
500: "#14a74b",
600: "#0f8a3d",
700: "#0c6f33",
800: "#095a2a",
900: "#064622",
950: "#032814",
},
success: {
DEFAULT: "#22c55e",