update ui

This commit is contained in:
2026-01-20 12:26:19 +07:00
parent c4244c1097
commit 21950753ab
13 changed files with 54 additions and 37 deletions

4
components.d.ts vendored
View File

@@ -22,6 +22,7 @@ declare module 'vue' {
DashboardSidebar: typeof import('./src/components/dashboard/DashboardSidebar.vue')['default']
DashboardTopbar: typeof import('./src/components/dashboard/DashboardTopbar.vue')['default']
EmptyState: typeof import('./src/components/dashboard/EmptyState.vue')['default']
FloatLabel: typeof import('primevue/floatlabel')['default']
HardDriveUpload: typeof import('./src/components/icons/HardDriveUpload.vue')['default']
Home: typeof import('./src/components/icons/Home.vue')['default']
IconField: typeof import('primevue/iconfield')['default']
@@ -36,6 +37,7 @@ declare module 'vue' {
RootLayout: typeof import('./src/components/RootLayout.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Select: typeof import('primevue/select')['default']
StatsCard: typeof import('./src/components/dashboard/StatsCard.vue')['default']
TestIcon: typeof import('./src/components/icons/TestIcon.vue')['default']
Toast: typeof import('primevue/toast')['default']
@@ -57,6 +59,7 @@ declare global {
const DashboardSidebar: typeof import('./src/components/dashboard/DashboardSidebar.vue')['default']
const DashboardTopbar: typeof import('./src/components/dashboard/DashboardTopbar.vue')['default']
const EmptyState: typeof import('./src/components/dashboard/EmptyState.vue')['default']
const FloatLabel: typeof import('primevue/floatlabel')['default']
const HardDriveUpload: typeof import('./src/components/icons/HardDriveUpload.vue')['default']
const Home: typeof import('./src/components/icons/Home.vue')['default']
const IconField: typeof import('primevue/iconfield')['default']
@@ -71,6 +74,7 @@ declare global {
const RootLayout: typeof import('./src/components/RootLayout.vue')['default']
const RouterLink: typeof import('vue-router')['RouterLink']
const RouterView: typeof import('vue-router')['RouterView']
const Select: typeof import('primevue/select')['default']
const StatsCard: typeof import('./src/components/dashboard/StatsCard.vue')['default']
const TestIcon: typeof import('./src/components/icons/TestIcon.vue')['default']
const Toast: typeof import('primevue/toast')['default']

View File

@@ -587,7 +587,14 @@ export class Api<
},
params: RequestParams = {},
) =>
this.request<ResponseResponse, ResponseResponse>({
this.request<ResponseResponse & {
data: {
limit: number;
page: number;
total: number;
videos: ModelVideo[];
}
}, ResponseResponse>({
path: `/videos`,
method: "GET",
query: query,
@@ -674,6 +681,6 @@ export class Api<
export const client = new Api({
baseUrl: 'r',
// baseUrl: 'https://carey-novelty-various-manufacturers.trycloudflare.com',
// baseUrl: 'https://interesting-atmosphere-encryption-value.trycloudflare.com',
customFetch
});

View File

@@ -10,7 +10,7 @@ export const customFetch = async (url: string, options: RequestInit) => {
Object.assign(options, {
headers: c.req.header()
});
const res = await fetch(["https://carey-novelty-various-manufacturers.trycloudflare.com", url.replace(/r\//, '')].join('/'), options);
const res = await fetch(["https://interesting-atmosphere-encryption-value.trycloudflare.com", url.replace(/r\//, '')].join('/'), options);
console.log('Fetching URL:', res);
res.headers.forEach((value, key) => {
c.header(key, value);

View File

@@ -35,9 +35,11 @@ const props = defineProps<Props>();
<button
v-if="actionLabel && onAction"
@click="onAction"
class="px-6 py-3 bg-primary hover:bg-primary-600 text-white rounded-lg font-medium transition-colors press-animated flex items-center gap-2"
class="btn btn-outline-primary press-animated flex items-center gap-2"
>
<span class="i-heroicons-plus w-5 h-5" />
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
{{ actionLabel }}
</button>

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { cn } from '@/lib/utils';
import { VNode } from 'vue';
interface Breadcrumb {
label: string;
@@ -8,7 +9,7 @@ interface Breadcrumb {
interface Action {
label: string;
icon?: string;
icon?: string | VNode;
variant?: 'primary' | 'secondary' | 'danger';
onClick: () => void;
}
@@ -76,7 +77,11 @@ const getButtonClass = (variant?: string) => {
@click="action.onClick"
:class="getButtonClass(action.variant)"
>
<span v-if="action.icon" :class="[action.icon, 'w-5 h-5']" />
<component
v-if="action.icon"
:is="action.icon"
class="w-5 h-5"
/>
{{ action.label }}
</button>
</div>

View File

@@ -15,5 +15,5 @@
</svg>
</template>
<script lang="ts" setup>
defineProps<{ class?: string, filled?: boolean }>();
defineProps<{ filled?: boolean }>();
</script>

View File

@@ -14,5 +14,5 @@
</svg>
</template>
<script lang="ts" setup>
defineProps<{ class?: string, filled?: boolean }>();
defineProps<{ filled?: boolean }>();
</script>

View File

@@ -3,5 +3,5 @@
<svg xmlns="http://www.w3.org/2000/svg" v-else viewBox="-10 -194 532 404"><path d="M448-136c9 0 16 7 16 16v32H48v-32c0-9 7-16 16-16h384zm16 112v160c0 9-7 16-16 16H64c-9 0-16-7-16-16V-24h416zM64-184c-35 0-64 29-64 64v256c0 35 29 64 64 64h384c35 0 64-29 64-64v-256c0-35-29-64-64-64H64zM80 96c0 13 11 24 24 24h48c13 0 24-11 24-24s-11-24-24-24h-48c-13 0-24 11-24 24zm144 0c0 13 11 24 24 24h64c13 0 24-11 24-24s-11-24-24-24h-64c-13 0-24 11-24 24z" fill="#1e3050"/></svg>
</template>
<script lang="ts" setup>
defineProps<{ class?: string, filled?: boolean }>();
defineProps<{ filled?: boolean }>();
</script>

View File

@@ -13,5 +13,5 @@
</svg>
</template>
<script lang="ts" setup>
defineProps<{ class?: string, filled?: boolean }>();
defineProps<{ filled?: boolean }>();
</script>

View File

@@ -15,5 +15,5 @@
</svg>
</template>
<script lang="ts" setup>
defineProps<{ class?: string, filled?: boolean }>();
defineProps<{ filled?: boolean }>();
</script>

View File

@@ -12,5 +12,5 @@
</svg>
</template>
<script lang="ts" setup>
defineProps<{ class?: string, filled?: boolean }>();
defineProps<{ filled?: boolean }>();
</script>

View File

@@ -32,7 +32,7 @@ app.use(cors(), async (c, next) => {
return next()
}
const url = new URL(c.req.url)
url.host = 'carey-novelty-various-manufacturers.trycloudflare.com'
url.host = 'interesting-atmosphere-encryption-value.trycloudflare.com'
url.protocol = 'https:'
url.pathname = path.replace(/^\/r/, '') || '/'
url.port = ''

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ref, onMounted, createStaticVNode } from 'vue';
import { useRouter } from 'vue-router';
import PageHeader from '@/components/dashboard/PageHeader.vue';
import EmptyState from '@/components/dashboard/EmptyState.vue';
@@ -12,7 +12,7 @@ const error = ref<string | null>(null);
const searchQuery = ref('');
const selectedStatus = ref<string>('all');
const viewMode = ref<'grid' | 'table'>('table');
const iconHoist = createStaticVNode(`<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15a4 4 0 004 4h10a4 4 0 004-4v-1a4 4 0 00-4-4H7a4 4 0 00-4 4v1zM16 7l-4-4m0 0L8 7m4-4v12" /></svg>`, 1)
// Pagination
const page = ref(1);
const limit = ref(20);
@@ -31,11 +31,11 @@ const fetchVideos = async () => {
error.value = null;
try {
const response = await client.videos.videosList({ page: page.value, limit: limit.value });
const body = response.data as any;
if (body.data && Array.isArray(body.data)) {
videos.value = body.data;
total.value = body.total || body.data.length;
const body = response.data.data
// console.log('Fetched videos:', body);
if (body.videos && Array.isArray(body.videos)) {
videos.value = body.videos;
total.value = body.total || body.videos.length;
} else if (Array.isArray(body)) {
videos.value = body;
total.value = body.length;
@@ -148,7 +148,8 @@ onMounted(() => {
:actions="[
{
label: 'Upload Video',
icon: 'i-heroicons-cloud-arrow-up',
// icon: 'i-heroicons-cloud-arrow-up',
icon: iconHoist,
variant: 'primary',
onClick: () => router.push('/upload')
}
@@ -161,28 +162,22 @@ onMounted(() => {
<!-- Search -->
<div class="flex-1">
<div class="relative">
<span class="absolute left-3 top-1/2 -translate-y-1/2 i-heroicons-magnifying-glass w-5 h-5 text-gray-400" />
<svg xmlns="http://www.w3.org/2000/svg" class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" viewBox="-10 -258 534 534"><path d="M384-40c0-97-79-176-176-176S32-137 32-40s79 176 176 176S384 57 384-40zm-41 158c-36 31-83 50-135 50C93 168 0 75 0-40s93-208 208-208 208 93 208 208c0 52-19 99-50 135l141 142c7 6 7 16 0 22-6 7-16 7-22 0L343 118z" fill="#1e3050"/></svg>
<input
v-model="searchQuery"
@keyup.enter="handleSearch"
type="text"
placeholder="Search videos by title or description..."
class="w-full pl-10 pr-4 py-2.5 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
/>
</div>
</div>
<!-- Status Filter -->
<select
v-model="selectedStatus"
@change="handleFilter"
class="px-4 py-2.5 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
>
<option v-for="option in statusOptions" :key="option.value" :value="option.value">
{{ option.label }}
</option>
</select>
<FloatLabel class="w-full md:w-56" variant="on">
<Select v-model="selectedStatus" inputId="on_label" :options="statusOptions" optionLabel="label" optionValue="value" class="w-full" />
<label for="on_label">Status</label>
</FloatLabel>
<!-- View Mode Toggle -->
<div class="flex items-center gap-2 bg-gray-100 rounded-lg p-1">
<button
@@ -193,7 +188,9 @@ onMounted(() => {
]"
title="Table view"
>
<span class="i-heroicons-list-bullet w-5 h-5" :class="viewMode === 'table' ? 'text-primary' : 'text-gray-600'" />
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" :class="viewMode === 'table' ? 'text-primary' : 'text-gray-600'" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" />
</svg>
</button>
<button
@click="viewMode = 'grid'"
@@ -203,7 +200,9 @@ onMounted(() => {
]"
title="Grid view"
>
<span class="i-heroicons-squares-2x2 w-5 h-5" :class="viewMode === 'grid' ? 'text-primary' : 'text-gray-600'" />
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" :class="viewMode === 'grid' ? 'text-primary' : 'text-gray-600'" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4h6v6H4V4zm0 10h6v6H4v-6zm10-10h6v6h-6V4zm0 10h6v6h-6v-6z" />
</svg>
</button>
</div>
</div>
@@ -228,7 +227,7 @@ onMounted(() => {
v-else-if="videos.length === 0"
title="No videos found"
description="You haven't uploaded any videos yet. Start by uploading your first video!"
icon="i-heroicons-film"
imageUrl="https://cdn-icons-png.flaticon.com/512/7486/7486747.png"
actionLabel="Upload Video"
:onAction="() => router.push('/upload')"
/>