feat: update icon components to support filled state and improve upload page layout
- Refactored HardDriveUpload, Home, Layout, LinkIcon, Upload, and Video components to include a 'filled' prop for conditional rendering of SVGs. - Enhanced the Upload.vue page with a more structured layout, including a PageHeader and improved button interactions for local and remote upload modes. - Added visual feedback for upload tips and improved accessibility with better button labeling. - Updated the upload queue display and added loading states for files being fetched from external sources.
This commit is contained in:
@@ -673,6 +673,7 @@ export class Api<
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const client = new Api({
|
export const client = new Api({
|
||||||
baseUrl: 'http://localhost:8080',
|
baseUrl: 'r',
|
||||||
|
// baseUrl: 'https://carey-novelty-various-manufacturers.trycloudflare.com',
|
||||||
customFetch
|
customFetch
|
||||||
});
|
});
|
||||||
@@ -10,7 +10,8 @@ export const customFetch = async (url: string, options: RequestInit) => {
|
|||||||
Object.assign(options, {
|
Object.assign(options, {
|
||||||
headers: c.req.header()
|
headers: c.req.header()
|
||||||
});
|
});
|
||||||
const res = await fetch(url, options);
|
const res = await fetch(["https://carey-novelty-various-manufacturers.trycloudflare.com", url.replace(/r\//, '')].join('/'), options);
|
||||||
|
console.log('Fetching URL:', res);
|
||||||
res.headers.forEach((value, key) => {
|
res.headers.forEach((value, key) => {
|
||||||
c.header(key, value);
|
c.header(key, value);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,11 +6,8 @@ import Video from "@/components/icons/Video.vue";
|
|||||||
import Credit from "@/components/icons/Credit.vue";
|
import Credit from "@/components/icons/Credit.vue";
|
||||||
import Upload from "./icons/Upload.vue";
|
import Upload from "./icons/Upload.vue";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useAuthStore } from "@/stores/auth";
|
|
||||||
import { createStaticVNode } from "vue";
|
import { createStaticVNode } from "vue";
|
||||||
|
|
||||||
const auth = useAuthStore();
|
|
||||||
|
|
||||||
const className = ":uno: w-12 h-12 p-2 rounded-2xl hover:bg-primary/15 flex press-animated items-center justify-center";
|
const className = ":uno: w-12 h-12 p-2 rounded-2xl hover:bg-primary/15 flex press-animated items-center justify-center";
|
||||||
const homeHoist = createStaticVNode(`<img class="h-8 w-8" src="/apple-touch-icon.png" alt="Logo" />`, 1);
|
const homeHoist = createStaticVNode(`<img class="h-8 w-8" src="/apple-touch-icon.png" alt="Logo" />`, 1);
|
||||||
const profileHoist = createStaticVNode(`<div class="h-[38px] w-[38px] rounded-full m-a ring-2 ring flex press-animated">
|
const profileHoist = createStaticVNode(`<div class="h-[38px] w-[38px] rounded-full m-a ring-2 ring flex press-animated">
|
||||||
@@ -33,7 +30,7 @@ const links = [
|
|||||||
<component :is="i.type === 'a' ? 'router-link' : 'div'" v-for="i in links" :key="i.label"
|
<component :is="i.type === 'a' ? 'router-link' : 'div'" v-for="i in links" :key="i.label"
|
||||||
v-bind="i.type === 'a' ? { to: i.href } : {}" v-tooltip="i.label"
|
v-bind="i.type === 'a' ? { to: i.href } : {}" v-tooltip="i.label"
|
||||||
:class="cn(i.className, $route.path === i.href && 'bg-primary/15')">
|
:class="cn(i.className, $route.path === i.href && 'bg-primary/15')">
|
||||||
<component :is="i.icon" :filled="$route.path === i.href" />
|
<component :is="i.icon" class="w-6 h-6" :filled="$route.path === i.href" />
|
||||||
</component>
|
</component>
|
||||||
</header>
|
</header>
|
||||||
<main class="flex flex-1 overflow-hidden md:ps-18">
|
<main class="flex flex-1 overflow-hidden md:ps-18">
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
interface Breadcrumb {
|
interface Breadcrumb {
|
||||||
label: string;
|
label: string;
|
||||||
to?: string;
|
to?: string;
|
||||||
@@ -36,7 +38,7 @@ const getButtonClass = (variant?: string) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="page-header mb-6">
|
<div :class="cn('page-header mb-6')">
|
||||||
<!-- Breadcrumb -->
|
<!-- Breadcrumb -->
|
||||||
<nav v-if="breadcrumbs && breadcrumbs.length" class="flex items-center gap-2 text-sm mb-2">
|
<nav v-if="breadcrumbs && breadcrumbs.length" class="flex items-center gap-2 text-sm mb-2">
|
||||||
<template v-for="(crumb, index) in breadcrumbs" :key="index">
|
<template v-for="(crumb, index) in breadcrumbs" :key="index">
|
||||||
@@ -51,8 +53,12 @@ const getButtonClass = (variant?: string) => {
|
|||||||
|
|
||||||
<span
|
<span
|
||||||
v-if="index < breadcrumbs.length - 1"
|
v-if="index < breadcrumbs.length - 1"
|
||||||
class="i-heroicons-chevron-right w-4 h-4 text-gray-400"
|
class="w-4 h-4 text-gray-400"
|
||||||
/>
|
>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" v-if="filled" height="24" width="24" viewBox="0 0 468 532">
|
<svg xmlns="http://www.w3.org/2000/svg" v-if="filled" viewBox="0 0 468 532">
|
||||||
<path
|
<path
|
||||||
d="M66 378h337l-13-22c-24-40-36-85-36-131v-15c0-66-54-120-120-120s-120 54-120 120v15c0 46-12 91-35 131l-13 22z"
|
d="M66 378h337l-13-22c-24-40-36-85-36-131v-15c0-66-54-120-120-120s-120 54-120 120v15c0 46-12 91-35 131l-13 22z"
|
||||||
fill="#a6acb9" />
|
fill="#a6acb9" />
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
d="M234 10c-13 0-24 11-24 24v10C129 55 66 125 66 210v15c0 37-10 74-29 107l-22 37c-3 6-5 13-5 19 0 21 17 38 38 38h372c21 0 38-17 38-38 0-6-2-13-5-19l-22-37c-19-33-29-70-29-108v-14c0-85-63-155-144-166V34c0-13-11-24-24-24zm168 368H66l12-22c24-40 36-85 36-131v-15c0-66 54-120 120-120s120 54 120 120v15c0 46 12 91 36 131l12 22zm-236 96c10 28 37 48 68 48s58-20 68-48H166z"
|
d="M234 10c-13 0-24 11-24 24v10C129 55 66 125 66 210v15c0 37-10 74-29 107l-22 37c-3 6-5 13-5 19 0 21 17 38 38 38h372c21 0 38-17 38-38 0-6-2-13-5-19l-22-37c-19-33-29-70-29-108v-14c0-85-63-155-144-166V34c0-13-11-24-24-24zm168 368H66l12-22c24-40 36-85 36-131v-15c0-66 54-120 120-120s120 54 120 120v15c0 46 12 91 36 131l12 22zm-236 96c10 28 37 48 68 48s58-20 68-48H166z"
|
||||||
fill="#1e3050" />
|
fill="#1e3050" />
|
||||||
</svg>
|
</svg>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" v-else height="24" viewBox="-10 -258 468 532">
|
<svg xmlns="http://www.w3.org/2000/svg" v-else viewBox="-10 -258 468 532">
|
||||||
<path
|
<path
|
||||||
d="M224-248c-13 0-24 11-24 24v10C119-203 56-133 56-48v15C56 4 46 41 27 74L5 111c-3 6-5 13-5 19 0 21 17 38 38 38h372c21 0 38-17 38-38 0-6-2-13-5-19l-22-37c-19-33-29-70-29-108v-14c0-85-63-155-144-166v-10c0-13-11-24-24-24zm168 368H56l12-22c24-40 36-85 36-131v-15c0-66 54-120 120-120s120 54 120 120v15c0 46 12 91 36 131l12 22zm-236 96c10 28 37 48 68 48s58-20 68-48H156z"
|
d="M224-248c-13 0-24 11-24 24v10C119-203 56-133 56-48v15C56 4 46 41 27 74L5 111c-3 6-5 13-5 19 0 21 17 38 38 38h372c21 0 38-17 38-38 0-6-2-13-5-19l-22-37c-19-33-29-70-29-108v-14c0-85-63-155-144-166v-10c0-13-11-24-24-24zm168 368H56l12-22c24-40 36-85 36-131v-15c0-66 54-120 120-120s120 54 120 120v15c0 46 12 91 36 131l12 22zm-236 96c10 28 37 48 68 48s58-20 68-48H156z"
|
||||||
fill="#1e3050" />
|
fill="#1e3050" />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" v-if="filled" width="24" viewBox="0 0 532 404"><path d="M10 74c0-35 29-64 64-64h384c35 0 64 29 64 64v32H10V74zm0 96h512v160c0 35-29 64-64 64H74c-35 0-64-29-64-64V170zm64 136c0 13 11 24 24 24h48c13 0 24-11 24-24s-11-24-24-24H98c-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="#a6acb9"/><path d="M10 106h512v64H10zm0 0z" fill="#1e3050"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" v-if="filled" viewBox="0 0 532 404"><path d="M10 74c0-35 29-64 64-64h384c35 0 64 29 64 64v32H10V74zm0 96h512v160c0 35-29 64-64 64H74c-35 0-64-29-64-64V170zm64 136c0 13 11 24 24 24h48c13 0 24-11 24-24s-11-24-24-24H98c-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="#a6acb9"/><path d="M10 106h512v64H10zm0 0z" fill="#1e3050"/></svg>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" v-else width="24" 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>
|
<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>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineProps<{ class?: string, filled?: boolean }>();
|
defineProps<{ class?: string, filled?: boolean }>();
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 468 503"><path d="M10 397v32c0 35 29 64 64 64h320c35 0 64-29 64-64v-32c0-35-29-64-64-64H266v32c0 18-14 32-32 32s-32-14-32-32v-32H74c-35 0-64 29-64 64zm392 16c0 13-11 24-24 24s-24-11-24-24 11-24 24-24 24 11 24 24z" fill="#a6acb9"/><path d="M234 397c18 0 32-14 32-32V122l41 42c13 12 33 12 46 0 12-13 12-33 0-46l-96-96c-13-12-33-12-46 0l-96 96c-12 13-12 33 0 46 13 12 33 12 46 0l41-42v243c0 18 14 32 32 32z" fill="#1e3050"/></svg>
|
<svg v-if="filled" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 468 503"><path d="M10 397v32c0 35 29 64 64 64h320c35 0 64-29 64-64v-32c0-35-29-64-64-64H266v32c0 18-14 32-32 32s-32-14-32-32v-32H74c-35 0-64 29-64 64zm392 16c0 13-11 24-24 24s-24-11-24-24 11-24 24-24 24 11 24 24z" fill="#a6acb9"/><path d="M234 397c18 0 32-14 32-32V122l41 42c13 12 33 12 46 0 12-13 12-33 0-46l-96-96c-13-12-33-12-46 0l-96 96c-12 13-12 33 0 46 13 12 33 12 46 0l41-42v243c0 18 14 32 32 32z" fill="#1e3050"/></svg>
|
||||||
|
<svg v-else xmlns="http://www.w3.org/2000/svg" viewBox="-10 -260 468 502"><path d="M248 80c0 13-11 24-24 24s-24-11-24-24v-246l-63 63c-9 9-25 9-34 0s-9-25 0-34l104-104c9-9 25-9 34 0l104 104c9 9 9 25 0 34s-25 9-34 0l-63-63V80zm-96-8H64c-9 0-16 7-16 16v80c0 9 7 16 16 16h320c9 0 16-7 16-16V88c0-9-7-16-16-16h-88V24h88c35 0 64 29 64 64v80c0 35-29 64-64 64H64c-35 0-64-29-64-64V88c0-35 29-64 64-64h88v48zm168 56c0-13 11-24 24-24s24 11 24 24-11 24-24 24-24-11-24-24z" fill="#1e3050"/></svg>
|
||||||
</template>
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
defineProps<{ filled?: boolean }>();
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" v-if="filled" class="v-mid m-a" height="24" width="24"
|
<svg xmlns="http://www.w3.org/2000/svg" v-if="filled" viewBox="0 0 539 535">
|
||||||
viewBox="0 0 539 535">
|
|
||||||
<path d="M61 281c2-1 4-3 6-5L269 89l202 187c2 2 4 4 6 5v180c0 35-29 64-64 64H125c-35 0-64-29-64-64V281z"
|
<path d="M61 281c2-1 4-3 6-5L269 89l202 187c2 2 4 4 6 5v180c0 35-29 64-64 64H125c-35 0-64-29-64-64V281z"
|
||||||
fill="#a6acb9" />
|
fill="#a6acb9" />
|
||||||
<path
|
<path
|
||||||
d="M247 22c13-12 32-12 44 0l224 208c13 12 13 32 1 45s-32 14-45 2L269 89 67 276c-13 12-33 12-45-1s-12-33 1-45L247 22z"
|
d="M247 22c13-12 32-12 44 0l224 208c13 12 13 32 1 45s-32 14-45 2L269 89 67 276c-13 12-33 12-45-1s-12-33 1-45L247 22z"
|
||||||
fill="#1e3050" />
|
fill="#1e3050" />
|
||||||
</svg>
|
</svg>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" v-else class="v-mid m-a" height="24" width="24" viewBox="-11 -259 535 533">
|
<svg xmlns="http://www.w3.org/2000/svg" v-else viewBox="-11 -259 535 533">
|
||||||
<path
|
<path
|
||||||
d="M272-242c-9-8-23-8-32 0L8-34C-2-25-3-10 6 0s24 11 34 2l8-7v205c0 35 29 64 64 64h288c35 0 64-29 64-64V-5l8 7c10 9 25 8 34-2s8-25-2-34L272-242zM416-48v248c0 9-7 16-16 16H112c-9 0-16-7-16-16V-48l160-144L416-48z"
|
d="M272-242c-9-8-23-8-32 0L8-34C-2-25-3-10 6 0s24 11 34 2l8-7v205c0 35 29 64 64 64h288c35 0 64-29 64-64V-5l8 7c10 9 25 8 34-2s8-25-2-34L272-242zM416-48v248c0 9-7 16-16 16H112c-9 0-16-7-16-16V-48l160-144L416-48z"
|
||||||
fill="#1e3050" />
|
fill="#1e3050" />
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 596 404"><path d="M74 170v64c0 53 43 96 96 96h96v64h64v-64h96c53 0 96-43 96-96v-64c0-53-43-96-96-96h-96V10h-64v64h-96c-53 0-96 43-96 96zm96 0h256v64H170v-64z" fill="#a6acb9"/><path d="M170 10C82 10 10 82 10 170v64c0 88 72 160 160 160h96v-64h-96c-53 0-96-43-96-96v-64c0-53 43-96 96-96h96V10h-96zm256 384c88 0 160-72 160-160v-64c0-88-72-160-160-160h-96v64h96c53 0 96 43 96 96v64c0 53-43 96-96 96h-96v64h96zM202 170h-32v64h256v-64H202z" fill="#1e3050"/></svg>
|
<svg v-if="filled" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 596 404"><path d="M74 170v64c0 53 43 96 96 96h96v64h64v-64h96c53 0 96-43 96-96v-64c0-53-43-96-96-96h-96V10h-64v64h-96c-53 0-96 43-96 96zm96 0h256v64H170v-64z" fill="#a6acb9"/><path d="M170 10C82 10 10 82 10 170v64c0 88 72 160 160 160h96v-64h-96c-53 0-96-43-96-96v-64c0-53 43-96 96-96h96V10h-96zm256 384c88 0 160-72 160-160v-64c0-88-72-160-160-160h-96v64h96c53 0 96 43 96 96v64c0 53-43 96-96 96h-96v64h96zM202 170h-32v64h256v-64H202z" fill="#1e3050"/></svg>
|
||||||
|
<svg v-else xmlns="http://www.w3.org/2000/svg" viewBox="-10 -194 596 404"><path d="M160-184C72-184 0-112 0-24v64c0 88 72 160 160 160h96v-64h-96c-53 0-96-43-96-96v-64c0-53 43-96 96-96h96v-64h-96zm256 384c88 0 160-72 160-160v-64c0-88-72-160-160-160h-96v64h96c53 0 96 43 96 96v64c0 53-43 96-96 96h-96v64h96zM192-24h-32v64h256v-64H192z" fill="#1e3050"/></svg>
|
||||||
</template>
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
defineProps<{ filled?: boolean }>();
|
||||||
|
</script>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" v-if="filled" width="28" viewBox="0 0 596 468">
|
<svg xmlns="http://www.w3.org/2000/svg" v-if="filled" class="min-w-[28px]" viewBox="0 0 596 468">
|
||||||
<path
|
<path
|
||||||
d="M10 314c0-63 41-117 98-136-1-8-2-16-2-24 0-79 65-144 144-144 55 0 104 31 128 77 14-8 30-13 48-13 53 0 96 43 96 96 0 16-4 31-10 44 44 20 74 64 74 116 0 71-57 128-128 128H154c-79 0-144-64-144-144zm199-73c-9 9-9 25 0 34s25 9 34 0l31-31v102c0 13 11 24 24 24s24-11 24-24V244l31 31c9 9 25 9 34 0s9-25 0-34l-72-72c-10-9-25-9-34 0l-72 72z"
|
d="M10 314c0-63 41-117 98-136-1-8-2-16-2-24 0-79 65-144 144-144 55 0 104 31 128 77 14-8 30-13 48-13 53 0 96 43 96 96 0 16-4 31-10 44 44 20 74 64 74 116 0 71-57 128-128 128H154c-79 0-144-64-144-144zm199-73c-9 9-9 25 0 34s25 9 34 0l31-31v102c0 13 11 24 24 24s24-11 24-24V244l31 31c9 9 25 9 34 0s9-25 0-34l-72-72c-10-9-25-9-34 0l-72 72z"
|
||||||
fill="#a6acb9" />
|
fill="#a6acb9" />
|
||||||
@@ -7,12 +7,12 @@
|
|||||||
d="M281 169c9-9 25-9 34 0l72 72c9 9 9 25 0 34s-25 9-34 0l-31-31v102c0 13-11 24-24 24s-24-11-24-24V244l-31 31c-9 9-25 9-34 0s-9-25 0-34l72-72z"
|
d="M281 169c9-9 25-9 34 0l72 72c9 9 9 25 0 34s-25 9-34 0l-31-31v102c0 13-11 24-24 24s-24-11-24-24V244l-31 31c-9 9-25 9-34 0s-9-25 0-34l72-72z"
|
||||||
fill="#1e3050" />
|
fill="#1e3050" />
|
||||||
</svg>
|
</svg>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" v-else width="28" viewBox="-10 -226 596 468">
|
<svg xmlns="http://www.w3.org/2000/svg" v-else class="min-w-[28px]" viewBox="-10 -226 596 468">
|
||||||
<path
|
<path
|
||||||
d="M240-216c-88 0-160 72-160 160 0 5 0 10 1 15C33-18 0 31 0 88c0 80 65 144 144 144h304c71 0 128-57 128-128 0-50-28-93-70-114 4-12 6-25 6-38 0-66-54-120-120-120-11 0-23 2-33 5-30-33-72-53-119-53zM128-56c0-62 50-112 112-112 38 0 71 19 91 47 7 10 20 13 30 8 9-4 20-7 31-7 40 0 72 32 72 72 0 14-4 27-11 38-4 7-5 15-2 22s9 13 16 14c35 9 61 41 61 78 0 44-36 80-80 80H144c-53 0-96-43-96-96 0-43 28-79 67-91 11-4 18-16 16-29-2-7-3-16-3-24zm177 7c-9-9-25-9-34 0l-64 64c-9 9-9 25 0 34 10 9 25 9 34 0l23-23v86c0 13 11 24 24 24s24-11 24-24V26l23 23c9 9 25 9 34 0s9-25 0-34l-64-64z"
|
d="M240-216c-88 0-160 72-160 160 0 5 0 10 1 15C33-18 0 31 0 88c0 80 65 144 144 144h304c71 0 128-57 128-128 0-50-28-93-70-114 4-12 6-25 6-38 0-66-54-120-120-120-11 0-23 2-33 5-30-33-72-53-119-53zM128-56c0-62 50-112 112-112 38 0 71 19 91 47 7 10 20 13 30 8 9-4 20-7 31-7 40 0 72 32 72 72 0 14-4 27-11 38-4 7-5 15-2 22s9 13 16 14c35 9 61 41 61 78 0 44-36 80-80 80H144c-53 0-96-43-96-96 0-43 28-79 67-91 11-4 18-16 16-29-2-7-3-16-3-24zm177 7c-9-9-25-9-34 0l-64 64c-9 9-9 25 0 34 10 9 25 9 34 0l23-23v86c0 13 11 24 24 24s24-11 24-24V26l23 23c9 9 25 9 34 0s9-25 0-34l-64-64z"
|
||||||
fill="#1e3050" />
|
fill="#1e3050" />
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineProps<{ class?: string, filled?: boolean }>();
|
defineProps<{ filled?: boolean }>();
|
||||||
</script>
|
</script>
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" v-if="filled" width="24" viewBox="0 0 532 404">
|
<svg xmlns="http://www.w3.org/2000/svg" v-if="filled" viewBox="0 0 532 404">
|
||||||
<path d="M10 74v256c0 35 29 64 64 64h256c35 0 64-29 64-64V74c0-35-29-64-64-64H74c-35 0-64 29-64 64z"
|
<path d="M10 74v256c0 35 29 64 64 64h256c35 0 64-29 64-64V74c0-35-29-64-64-64H74c-35 0-64 29-64 64z"
|
||||||
fill="#a6acb9" />
|
fill="#a6acb9" />
|
||||||
<path d="M394 135v134l90 72c4 3 9 5 14 5 13 0 24-11 24-24V82c0-13-11-24-24-24-5 0-10 2-14 5l-90 72z"
|
<path d="M394 135v134l90 72c4 3 9 5 14 5 13 0 24-11 24-24V82c0-13-11-24-24-24-5 0-10 2-14 5l-90 72z"
|
||||||
fill="#1e3050" />
|
fill="#1e3050" />
|
||||||
</svg>
|
</svg>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" v-else width="24" viewBox="22 -194 564 404">
|
<svg xmlns="http://www.w3.org/2000/svg" v-else viewBox="22 -194 564 404">
|
||||||
<path
|
<path
|
||||||
d="M96-136c-9 0-16 7-16 16v256c0 9 7 16 16 16h256c9 0 16-7 16-16v-256c0-9-7-16-16-16H96zm-64 16c0-35 29-64 64-64h256c35 0 64 29 64 64v256c0 35-29 64-64 64H96c-35 0-64-29-64-64v-256zm506-11c4-3 9-5 14-5 13 0 24 11 24 24v240c0 13-11 24-24 24-5 0-10-2-14-5l-74-55V32l64 48V-64l-64 48v-60l74-55z"
|
d="M96-136c-9 0-16 7-16 16v256c0 9 7 16 16 16h256c9 0 16-7 16-16v-256c0-9-7-16-16-16H96zm-64 16c0-35 29-64 64-64h256c35 0 64 29 64 64v256c0 35-29 64-64 64H96c-35 0-64-29-64-64v-256zm506-11c4-3 9-5 14-5 13 0 24 11 24 24v240c0 13-11 24-24 24-5 0-10-2-14-5l-74-55V32l64 48V-64l-64 48v-60l74-55z"
|
||||||
fill="#1e3050" />
|
fill="#1e3050" />
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { renderSSRHead } from '@unhead/vue/server';
|
|||||||
import { buildBootstrapScript, getHrefFromManifest, loadCssByModules } from './lib/manifest';
|
import { buildBootstrapScript, getHrefFromManifest, loadCssByModules } from './lib/manifest';
|
||||||
import { contextStorage } from 'hono/context-storage';
|
import { contextStorage } from 'hono/context-storage';
|
||||||
import { cors } from "hono/cors";
|
import { cors } from "hono/cors";
|
||||||
import { firebaseAuthMiddleware, rpcServer } from './api/rpc';
|
import { endpoint, firebaseAuthMiddleware, rpcServer } from './api/rpc';
|
||||||
import isMobile from 'is-mobile';
|
import isMobile from 'is-mobile';
|
||||||
import { useAuthStore } from './stores/auth';
|
import { useAuthStore } from './stores/auth';
|
||||||
import { cssContent } from './lib/primeCssContent';
|
import { cssContent } from './lib/primeCssContent';
|
||||||
@@ -25,7 +25,20 @@ app.use(cors(), async (c, next) => {
|
|||||||
};
|
};
|
||||||
c.set("isMobile", isMobile({ ua }));
|
c.set("isMobile", isMobile({ ua }));
|
||||||
await next();
|
await next();
|
||||||
}, firebaseAuthMiddleware, rpcServer);
|
}, async (c, next) => {
|
||||||
|
const path = c.req.path
|
||||||
|
|
||||||
|
if (path !== '/r' && !path.startsWith('/r/')) {
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
const url = new URL(c.req.url)
|
||||||
|
url.host = 'carey-novelty-various-manufacturers.trycloudflare.com'
|
||||||
|
url.protocol = 'https:'
|
||||||
|
url.pathname = path.replace(/^\/r/, '') || '/'
|
||||||
|
url.port = ''
|
||||||
|
const req = new Request(url.toString(), c.req.raw)
|
||||||
|
return fetch(req)
|
||||||
|
});
|
||||||
app.get("/.well-known/*", (c) => {
|
app.get("/.well-known/*", (c) => {
|
||||||
return c.json({ ok: true });
|
return c.json({ ok: true });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,94 +1,146 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import PageHeader from '@/components/dashboard/PageHeader.vue';
|
||||||
|
import HardDriveUpload from '@/components/icons/HardDriveUpload.vue';
|
||||||
|
import LinkIcon from '@/components/icons/LinkIcon.vue';
|
||||||
import Upload from '@/components/icons/Upload.vue';
|
import Upload from '@/components/icons/Upload.vue';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
const mode = ref<'local' | 'remote'>('local');
|
||||||
|
const modeList: { id: 'local' | 'remote'; label: string; icon: any }[] = [
|
||||||
|
{
|
||||||
|
id: 'local',
|
||||||
|
label: 'Local Upload',
|
||||||
|
icon: HardDriveUpload
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'remote',
|
||||||
|
label: 'Remote URL',
|
||||||
|
icon: LinkIcon
|
||||||
|
}
|
||||||
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="flex-1 flex items-stretch">
|
<div class="flex-1 flex items-stretch">
|
||||||
|
<div class="flex-1 overflow-y-auto">
|
||||||
<div class="flex-1 p-8 lg:p-12 overflow-y-auto">
|
<PageHeader class="block" title="Upload Videos" description="Choose your preferred method to upload videos."
|
||||||
<div class="max-w-4xl mx-auto">
|
:breadcrumbs="[
|
||||||
|
{ label: 'Dashboard', to: '/' },
|
||||||
<header class="mb-10">
|
{ label: 'Upload Videos' }
|
||||||
<h1 class="text-3xl font-bold text-slate-900 tracking-tight">Tải lên Video</h1>
|
]" />
|
||||||
<p class="text-lg text-slate-500 mt-2">Chọn phương thức tải lên để bắt đầu xử lý.</p>
|
<div class="flex flex-col max-w-4xl mx-auto gap-4">
|
||||||
</header>
|
<div class="inline-flex bg-slate-100 p-1 rounded-2xl relative z-0 w-fit">
|
||||||
|
<div
|
||||||
<div class="inline-flex bg-slate-100 p-1 rounded-2xl mb-8 relative z-0">
|
:class="cn(':uno: absolute left-1 top-1 h-[calc(100%-8px)] w-[calc(50%-4px)] bg-white rounded-xl shadow-sm transition-all duration-300 ease-out -z-10', mode === 'local' ? 'translate-x-0' : 'translate-x-full')">
|
||||||
<div id="pill-bg" class="absolute left-1 top-1 h-[calc(100%-8px)] w-[calc(50%-4px)] bg-white rounded-xl shadow-sm transition-all duration-300 ease-out -z-10"></div>
|
</div>
|
||||||
<button onclick="switchView('local')" id="btn-local" class="flex items-center gap-2 px-6 py-3 text-sm font-semibold text-slate-900 rounded-xl transition-colors relative z-10">
|
<button v-for="value in modeList" @click="mode = value.id"
|
||||||
<hard-drive-upload class="w-5 h-5"/> Từ máy tính
|
:class="cn('flex items-center gap-2 px-6 py-3 text-sm rounded-xl transition-colors relative z-10', mode === value.id ? 'font-semibold text-slate-900' : 'font-medium text-slate-500 hover:text-slate-900 ')">
|
||||||
</button>
|
<component :is="value.icon" :filled="mode === value.id" class="w-5 h-5" /> {{ value.label }}
|
||||||
<button onclick="switchView('remote')" id="btn-remote" class="flex items-center gap-2 px-6 py-3 text-sm font-medium text-slate-500 hover:text-slate-900 rounded-xl transition-colors relative z-10">
|
|
||||||
<link-icon class="w-5 h-5"/> Remote URL
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="rounded-2xl border border-blue-500/30 bg-blue-500/10 p-3 flex items-start gap-3"><svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
class="lucide lucide-info h-5 w-5 text-blue-600 dark:text-blue-400 shrink-0 mt-0.5"
|
||||||
|
aria-hidden="true">
|
||||||
|
<circle cx="12" cy="12" r="10"></circle>
|
||||||
|
<path d="M12 16v-4"></path>
|
||||||
|
<path d="M12 8h.01"></path>
|
||||||
|
</svg>
|
||||||
|
<div class="flex-1 text-sm">
|
||||||
|
<p class="font-medium text-blue-900 dark:text-blue-100 mb-1">Tip: For fastest processing</p>
|
||||||
|
<p class="text-blue-800 dark:text-blue-200">Upload videos in <strong>H.264 video codec + AAC
|
||||||
|
audio codec</strong> format (e.g., MP4 with H.264/AAC). Videos in this format will be
|
||||||
|
processed much faster (seconds instead of minutes) because they don't need re-encoding.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Transition enter-active-class="transition-all duration-300 ease-in-out"
|
||||||
|
enter-from-class="opacity-0 transform translate-y-4"
|
||||||
|
enter-to-class="opacity-100 transform translate-y-0"
|
||||||
|
leave-active-class="transition-all duration-200 ease-in-out"
|
||||||
|
leave-from-class="opacity-100 transform translate-y-0"
|
||||||
|
leave-to-class="opacity-0 transform -translate-y-4" mode="out-in">
|
||||||
|
<div v-if="mode === 'local'">
|
||||||
|
<div class="relative group cursor-pointer">
|
||||||
|
<input type="file" multiple
|
||||||
|
class="absolute inset-0 w-full h-full opacity-0 z-20 cursor-pointer"
|
||||||
|
onchange="simulateUploadStart()">
|
||||||
|
|
||||||
<div id="view-local" class="transition-all duration-500 ease-[cubic-bezier(0.25,0.8,0.25,1)] opacity-100 translate-y-0">
|
<div
|
||||||
<div class="relative group cursor-pointer">
|
class="bg-gradient-to-tr from-slate-50 to-white rounded-[2rem] p-16 text-center border-2 border-dashed border-slate-200 group-hover:border-success/50 group-hover:shadow-soft transition-all duration-300 relative overflow-hidden">
|
||||||
<input type="file" multiple class="absolute inset-0 w-full h-full opacity-0 z-20 cursor-pointer" onchange="simulateUploadStart()">
|
|
||||||
|
|
||||||
<div class="bg-gradient-to-tr from-slate-50 to-white rounded-[2rem] p-16 text-center border-2 border-dashed border-slate-200 group-hover:border-success/50 group-hover:shadow-soft transition-all duration-300 relative overflow-hidden">
|
<div
|
||||||
|
class="absolute top-0 left-0 w-64 h-64 bg-indigo-100/40 rounded-full blur-3xl -translate-x-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-700">
|
||||||
<div class="absolute top-0 left-0 w-64 h-64 bg-indigo-100/40 rounded-full blur-3xl -translate-x-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-700"></div>
|
</div>
|
||||||
<div class="absolute bottom-0 right-0 w-64 h-64 bg-blue-100/40 rounded-full blur-3xl translate-x-1/2 translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-700"></div>
|
<div
|
||||||
|
class="absolute bottom-0 right-0 w-64 h-64 bg-blue-100/40 rounded-full blur-3xl translate-x-1/2 translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-700">
|
||||||
<div class="relative z-10 flex flex-col items-center">
|
|
||||||
<div class="w-24 h-24 mb-8 rounded-3xl bg-white shadow-soft flex items-center justify-center text-accent group-hover:scale-110 group-hover:shadow-card-hover transition-all duration-300 ring-4 ring-slate-50 group-hover:ring-indigo-50">
|
|
||||||
<Upload class="w-10 h-10"></Upload>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 class="text-2xl font-semibold text-slate-900 mb-3">Kéo thả video của bạn vào đây</h3>
|
<div class="relative z-10 flex flex-col items-center">
|
||||||
<p class="text-slate-500 text-base mb-8 max-w-md mx-auto leading-relaxed">Hỗ trợ tải nhiều file cùng lúc. Định dạng MP4, MOV, MKV. Tối đa 10GB mỗi file.</p>
|
<div
|
||||||
|
class="w-24 h-24 mb-8 rounded-3xl bg-white shadow-soft flex items-center justify-center text-accent group-hover:scale-110 group-hover:shadow-card-hover transition-all duration-300 ring-4 ring-slate-50 group-hover:ring-indigo-50">
|
||||||
|
<Upload filled class="w-10 h-10"></Upload>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span class="px-8 py-3.5 btn-lg btn-primary">
|
<h3 class="text-2xl font-semibold text-slate-900 mb-3">Drag and drop your videos here
|
||||||
<i data-lucide="folder-open" class="w-5 h-5"></i>
|
|
||||||
Duyệt file từ thiết bị
|
</h3>
|
||||||
</span>
|
<p class="text-slate-500 text-base mb-8 max-w-md mx-auto leading-relaxed">Supports uploading multiple files at once. Formats MP4, MOV, MKV. Up to 10GB per file.</p>
|
||||||
|
<span class="px-8 py-3.5 btn-lg btn-primary flex items-center gap-2">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="22 -226 570 436"><path d="M56-22 32 48v-200c0-35 29-64 64-64h139c14 0 27 5 39 13l38 29c5 4 12 6 19 6h117c36 0 64 29 64 64v16H147c-41 0-78 26-91 66zm422 222H99c-33 0-56-32-45-63L102-7c6-20 24-33 45-33h379c33 0 56 32 45 63l-48 144c-6 20-25 33-45 33z" fill="#fff"/></svg>
|
||||||
|
Choose Files
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="view-remote" class="hidden transition-all duration-500 ease-[cubic-bezier(0.25,0.8,0.25,1)] opacity-0 translate-y-4">
|
<div v-else>
|
||||||
<div class="bg-white rounded-[2rem] shadow-soft p-10 border border-slate-100/50">
|
<div class="bg-white rounded-[2rem] shadow-soft p-10 border border-slate-100/50">
|
||||||
<label class="block text-lg font-semibold text-slate-900 mb-4">Nhập đường dẫn Video</label>
|
<label class="block text-lg font-semibold text-slate-900 mb-4">Nhập đường dẫn Video</label>
|
||||||
|
|
||||||
<div class="flex gap-4 items-start">
|
<div class="flex gap-4 items-start">
|
||||||
<div class="flex-1 relative group">
|
<div class="flex-1 relative group">
|
||||||
<div class="absolute left-4 top-4 text-slate-400 group-focus-within:text-accent transition-colors">
|
<div
|
||||||
<i data-lucide="globe" class="w-6 h-6"></i>
|
class="absolute left-4 top-4 text-slate-400 group-focus-within:text-accent transition-colors">
|
||||||
|
<i data-lucide="globe" class="w-6 h-6"></i>
|
||||||
|
</div>
|
||||||
|
<textarea
|
||||||
|
placeholder="Dán một hoặc nhiều link (mỗi link một dòng)... https://drive.google.com/file/..."
|
||||||
|
class="w-full pl-12 pr-4 py-4 h-32 bg-slate-50/50 border-2 border-slate-100 rounded-2xl focus:border-accent focus:bg-white focus:ring-0 transition-all resize-none text-slate-800 placeholder:text-slate-400 text-base leading-relaxed font-medium"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<textarea placeholder="Dán một hoặc nhiều link (mỗi link một dòng)... https://drive.google.com/file/..."
|
</div>
|
||||||
class="w-full pl-12 pr-4 py-4 h-32 bg-slate-50/50 border-2 border-slate-100 rounded-2xl focus:border-accent focus:bg-white focus:ring-0 transition-all resize-none text-slate-800 placeholder:text-slate-400 text-base leading-relaxed font-medium"></textarea>
|
|
||||||
|
<div class="mt-6 flex items-center justify-between">
|
||||||
|
<div
|
||||||
|
class="flex items-center gap-2 text-sm text-slate-500 bg-slate-50 px-4 py-2 rounded-full">
|
||||||
|
<i data-lucide="info" class="w-4 h-4"></i> Tự động phát hiện Google Drive, Dropbox
|
||||||
|
</div>
|
||||||
|
<button onclick="simulateUploadStart()"
|
||||||
|
class="px-8 py-3.5 bg-slate-900 hover:bg-black text-white font-medium rounded-xl shadow-xl shadow-slate-200/50 transition-all active:scale-95 flex items-center gap-2">
|
||||||
|
<i data-lucide="sparkles" class="w-5 h-5 text-yellow-300"></i>
|
||||||
|
Import & Upload Videos
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-6 flex items-center justify-between">
|
|
||||||
<div class="flex items-center gap-2 text-sm text-slate-500 bg-slate-50 px-4 py-2 rounded-full">
|
|
||||||
<i data-lucide="info" class="w-4 h-4"></i> Tự động phát hiện Google Drive, Dropbox
|
|
||||||
</div>
|
|
||||||
<button onclick="simulateUploadStart()" class="px-8 py-3.5 bg-slate-900 hover:bg-black text-white font-medium rounded-xl shadow-xl shadow-slate-200/50 transition-all active:scale-95 flex items-center gap-2">
|
|
||||||
<i data-lucide="sparkles" class="w-5 h-5 text-yellow-300"></i>
|
|
||||||
Phân tích & Tải về
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Transition>
|
||||||
|
<div id="bulk-actions" class="mt-10 hidden opacity-0 translate-y-4 transition-all duration-500">
|
||||||
<div id="bulk-actions" class="mt-10 hidden opacity-0 translate-y-4 transition-all duration-500">
|
<div
|
||||||
<div class="p-6 bg-indigo-50/50 rounded-3xl border border-indigo-100/50 flex items-center justify-between">
|
class="p-6 bg-indigo-50/50 rounded-3xl border border-indigo-100/50 flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h4 class="text-lg font-semibold text-slate-900">Thiết lập nhanh</h4>
|
<h4 class="text-lg font-semibold text-slate-900">Thiết lập nhanh</h4>
|
||||||
<p class="text-slate-500 text-sm">Áp dụng cho 2 file đang chờ</p>
|
<p class="text-slate-500 text-sm">Áp dụng cho 2 file đang chờ</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<select class="px-4 py-2.5 bg-white border-2 border-slate-200 rounded-xl text-sm font-medium text-slate-700 focus:border-accent outline-none transition">
|
<select
|
||||||
|
class="px-4 py-2.5 bg-white border-2 border-slate-200 rounded-xl text-sm font-medium text-slate-700 focus:border-accent outline-none transition">
|
||||||
<option>Chọn chuyên mục...</option>
|
<option>Chọn chuyên mục...</option>
|
||||||
<option>Học tập</option>
|
<option>Học tập</option>
|
||||||
<option>Giải trí</option>
|
<option>Giải trí</option>
|
||||||
</select>
|
</select>
|
||||||
<select class="px-4 py-2.5 bg-white border-2 border-slate-200 rounded-xl text-sm font-medium text-slate-700 focus:border-accent outline-none transition">
|
<select
|
||||||
|
class="px-4 py-2.5 bg-white border-2 border-slate-200 rounded-xl text-sm font-medium text-slate-700 focus:border-accent outline-none transition">
|
||||||
<option>Công khai (Public)</option>
|
<option>Công khai (Public)</option>
|
||||||
<option>Riêng tư (Private)</option>
|
<option>Riêng tư (Private)</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -99,7 +151,7 @@ import Upload from '@/components/icons/Upload.vue';
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<aside class="w-[420px] bg-slate-50 border-l border-slate-100/80 flex flex-col h-[calc(100vh-64px)] sticky top-16">
|
<aside class="w-[420px] bg-gray-100 rounded-xl flex flex-col h-[calc(100svh-64px)] sticky top-16">
|
||||||
|
|
||||||
<div class="p-6 border-b border-slate-100/80 flex items-center justify-between shrink-0">
|
<div class="p-6 border-b border-slate-100/80 flex items-center justify-between shrink-0">
|
||||||
<div>
|
<div>
|
||||||
@@ -113,37 +165,46 @@ import Upload from '@/components/icons/Upload.vue';
|
|||||||
|
|
||||||
<div class="flex-1 overflow-y-auto scrollbar-thin p-6 space-y-5 relative" id="queue-list">
|
<div class="flex-1 overflow-y-auto scrollbar-thin p-6 space-y-5 relative" id="queue-list">
|
||||||
|
|
||||||
<div id="empty-queue" class="absolute inset-0 flex flex-col items-center justify-center p-8 text-center opacity-40">
|
<div id="empty-queue"
|
||||||
<img src="https://cdn-icons-png.flaticon.com/512/7486/7486747.png" alt="Empty" class="w-32 h-32 mb-4 grayscale opacity-50 drop-shadow-xl">
|
class="absolute inset-0 flex flex-col items-center justify-center p-8 text-center opacity-40">
|
||||||
<p class="text-slate-400 font-medium">Danh sách trống</p>
|
<img src="https://cdn-icons-png.flaticon.com/512/7486/7486747.png" alt="Empty"
|
||||||
|
class="w-32 h-32 mb-4 grayscale opacity-50 drop-shadow-xl">
|
||||||
|
<p class="text-slate-400 font-medium">Empty queue!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="queue-item hidden bg-white rounded-2xl p-5 shadow-soft border border-slate-100/50 relative group overflow-hidden transition-all hover:shadow-md">
|
<div
|
||||||
|
class="queue-item hidden bg-white rounded-2xl p-5 shadow-soft border border-slate-100/50 relative group overflow-hidden transition-all hover:shadow-md">
|
||||||
<div class="flex gap-4 relative z-10">
|
<div class="flex gap-4 relative z-10">
|
||||||
<div class="w-20 h-16 bg-slate-800 rounded-xl shrink-0 relative overflow-hidden shadow-sm">
|
<div class="w-20 h-16 bg-slate-800 rounded-xl shrink-0 relative overflow-hidden shadow-sm">
|
||||||
<div class="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent z-10"></div>
|
<div class="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent z-10"></div>
|
||||||
<img src="https://images.unsplash.com/photo-1611162617474-5b21e879e113?w=200&q=80" class="w-full h-full object-cover opacity-80" alt="">
|
<img src="https://images.unsplash.com/photo-1611162617474-5b21e879e113?w=200&q=80"
|
||||||
<div class="absolute bottom-1 left-2 z-20">
|
class="w-full h-full object-cover opacity-80" alt="">
|
||||||
|
<div class="absolute bottom-1 left-2 z-20">
|
||||||
<i data-lucide="play-circle" class="w-4 h-4 text-white/90"></i>
|
<i data-lucide="play-circle" class="w-4 h-4 text-white/90"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-1 min-w-0 py-0.5 flex flex-col justify-between">
|
<div class="flex-1 min-w-0 py-0.5 flex flex-col justify-between">
|
||||||
<div class="flex justify-between items-start gap-2">
|
<div class="flex justify-between items-start gap-2">
|
||||||
<h4 class="text-sm font-bold text-slate-800 truncate">Introduction_to_React_v18.mp4</h4>
|
<h4 class="text-sm font-bold text-slate-800 truncate">Introduction_to_React_v18.mp4</h4>
|
||||||
<button class="text-slate-300 hover:text-red-500 transition p-1 -mr-2 -mt-2 opacity-0 group-hover:opacity-100">
|
<button
|
||||||
|
class="text-slate-300 hover:text-red-500 transition p-1 -mr-2 -mt-2 opacity-0 group-hover:opacity-100">
|
||||||
<i data-lucide="x" class="w-4 h-4"></i>
|
<i data-lucide="x" class="w-4 h-4"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex justify-between text-xs text-slate-500 mb-1.5 font-medium">
|
<div class="flex justify-between text-xs text-slate-500 mb-1.5 font-medium">
|
||||||
<span class="flex items-center gap-1.5"><span class="w-2 h-2 rounded-full bg-accent animate-pulse"></span> Uploading...</span>
|
<span class="flex items-center gap-1.5"><span
|
||||||
|
class="w-2 h-2 rounded-full bg-accent animate-pulse"></span>
|
||||||
|
Uploading...</span>
|
||||||
<span class="text-accent font-bold">72%</span>
|
<span class="text-accent font-bold">72%</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-1.5 w-full bg-slate-100 rounded-full overflow-hidden relative">
|
<div class="h-1.5 w-full bg-slate-100 rounded-full overflow-hidden relative">
|
||||||
<div class="absolute inset-0 bg-accent/20 animate-pulse w-full"></div>
|
<div class="absolute inset-0 bg-accent/20 animate-pulse w-full"></div>
|
||||||
<div class="h-full bg-accent rounded-full w-[72%] relative z-10 shadow-[0_0_12px_rgba(99,102,241,0.6)] transition-all duration-500"></div>
|
<div
|
||||||
|
class="h-full bg-accent rounded-full w-[72%] relative z-10 shadow-[0_0_12px_rgba(99,102,241,0.6)] transition-all duration-500">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between mt-2 text-[11px] text-slate-400 font-medium">
|
<div class="flex justify-between mt-2 text-[11px] text-slate-400 font-medium">
|
||||||
<span>345 MB of 520 MB</span>
|
<span>345 MB of 520 MB</span>
|
||||||
@@ -154,24 +215,30 @@ import Upload from '@/components/icons/Upload.vue';
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="queue-item hidden bg-[#F0F3FF] rounded-2xl p-5 shadow-soft border border-indigo-100/50 relative overflow-hidden group transition-all hover:shadow-md">
|
<div
|
||||||
<div class="absolute inset-0 opacity-[0.03] bg-[radial-gradient(#6366F1_1px,transparent_1px)] [background-size:16px_16px]"></div>
|
class="queue-item hidden bg-[#F0F3FF] rounded-2xl p-5 shadow-soft border border-indigo-100/50 relative overflow-hidden group transition-all hover:shadow-md">
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 opacity-[0.03] bg-[radial-gradient(#6366F1_1px,transparent_1px)] [background-size:16px_16px]">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-4 relative z-10">
|
<div class="flex gap-4 relative z-10">
|
||||||
<div class="w-20 h-16 bg-indigo-100 rounded-xl shrink-0 flex items-center justify-center text-accent shadow-sm">
|
<div
|
||||||
|
class="w-20 h-16 bg-indigo-100 rounded-xl shrink-0 flex items-center justify-center text-accent shadow-sm">
|
||||||
<i data-lucide="link-2" class="w-8 h-8 opacity-80"></i>
|
<i data-lucide="link-2" class="w-8 h-8 opacity-80"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-1 min-w-0 py-1 flex flex-col justify-center">
|
<div class="flex-1 min-w-0 py-1 flex flex-col justify-center">
|
||||||
<div class="flex justify-between items-start gap-2">
|
<div class="flex justify-between items-start gap-2">
|
||||||
<h4 class="text-sm font-bold text-slate-800 truncate">Advanced_NodeJS_Patterns.mkv</h4>
|
<h4 class="text-sm font-bold text-slate-800 truncate">Advanced_NodeJS_Patterns.mkv</h4>
|
||||||
<button class="text-slate-400 hover:text-red-500 transition p-1 -mr-2 -mt-2 opacity-0 group-hover:opacity-100">
|
<button
|
||||||
|
class="text-slate-400 hover:text-red-500 transition p-1 -mr-2 -mt-2 opacity-0 group-hover:opacity-100">
|
||||||
<i data-lucide="x" class="w-4 h-4"></i>
|
<i data-lucide="x" class="w-4 h-4"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-3 mt-3">
|
<div class="flex items-center gap-3 mt-3">
|
||||||
<div class="flex items-center gap-2 text-xs font-bold text-indigo-600 bg-white py-1.5 px-3 rounded-lg shadow-sm">
|
<div
|
||||||
|
class="flex items-center gap-2 text-xs font-bold text-indigo-600 bg-white py-1.5 px-3 rounded-lg shadow-sm">
|
||||||
<i data-lucide="loader" class="w-3.5 h-3.5 animate-spin"></i>
|
<i data-lucide="loader" class="w-3.5 h-3.5 animate-spin"></i>
|
||||||
Fetching from Google Drive...
|
Fetching from Google Drive...
|
||||||
</div>
|
</div>
|
||||||
@@ -182,12 +249,13 @@ import Upload from '@/components/icons/Upload.vue';
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-6 border-t border-slate-100/80 bg-white shrink-0">
|
<div class="p-6 border-t-2 border-white rounded-b-2xl shrink-0">
|
||||||
<div class="flex items-center justify-between text-sm mb-4 font-medium">
|
<div class="flex items-center justify-between text-sm mb-4 font-medium">
|
||||||
<span class="text-slate-500">Tổng dung lượng:</span>
|
<span class="text-slate-500">Tổng dung lượng:</span>
|
||||||
<span class="text-slate-900">865 MB</span>
|
<span class="text-slate-900">865 MB</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="w-full py-4 bg-accent hover:bg-accentHover text-white text-sm font-bold rounded-2xl shadow-xl shadow-indigo-200 transition-transform active:scale-[0.98] flex items-center justify-center gap-2 disabled:opacity-70 disabled:cursor-not-allowed" id="btn-publish" disabled>
|
<button class="btn btn-outline-primary w-full flex items-center justify-center gap-2" id="btn-publish"
|
||||||
|
disabled>
|
||||||
<i data-lucide="check-circle-2" class="w-5 h-5"></i>
|
<i data-lucide="check-circle-2" class="w-5 h-5"></i>
|
||||||
Hoàn tất & Xuất bản (0)
|
Hoàn tất & Xuất bản (0)
|
||||||
</button>
|
</button>
|
||||||
@@ -195,5 +263,5 @@ import Upload from '@/components/icons/Upload.vue';
|
|||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
</main>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
Reference in New Issue
Block a user