update ui

This commit is contained in:
2026-01-27 17:53:25 +07:00
parent 7a1f5d5ae0
commit a9e5ea61f8
12 changed files with 73 additions and 63 deletions

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { cn } from '@/lib/utils';
import { VNode } from 'vue';
import { type Component, VNode } from 'vue';
interface Breadcrumb {
label: string;
@@ -15,7 +15,7 @@ interface Action {
}
interface Props {
title: string | VNode;
title: string | VNode | Component;
description?: string;
breadcrumbs?: Breadcrumb[];
actions?: Action[];

View File

@@ -74,7 +74,9 @@ app.get("*", async (c) => {
await stream.write("<!DOCTYPE html><html lang='en'><head>");
await stream.write("<base href='" + url.origin + "'/>");
await renderSSRHead(head).then((headString) => stream.write(headString.headTags.replace(/\n/g, "")));
await stream.write(`<link href="https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"rel="stylesheet"></link>`);
// await stream.write(`<link href="https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"rel="stylesheet"></link>`);
await stream.write(`<link rel="preconnect" href="https://fonts.googleapis.com">`);
await stream.write(`<link href="https://fonts.googleapis.com/css2?family=Google+Sans:ital,opsz,wght@0,17..18,400..700;1,17..18,400..700&display=swap" rel="stylesheet">`);
await stream.write('<link rel="icon" href="/favicon.ico" />');
await stream.write(buildBootstrapScript());
if (usedStyles.size > 0) {

View File

@@ -9,12 +9,12 @@
<div class="flex flex-col gap-1">
<label for="email" class="text-sm font-medium text-gray-700">Email address</label>
<InputText name="email" type="email" placeholder="you@example.com" fluid />
<InputText size="small" name="email" type="email" placeholder="you@example.com" fluid />
<Message v-if="$form.email?.invalid" severity="error" size="small" variant="simple">{{
$form.email.error?.message }}</Message>
</div>
<Button type="submit" label="Send Reset Link" fluid />
<Button type="submit" size="small" label="Send Reset Link" fluid />
<div class="text-center mt-2">
<router-link to="/login" replace
@@ -31,15 +31,15 @@
</template>
<script setup lang="ts">
import { reactive } from 'vue';
import { Form, type FormSubmitEvent } from '@primevue/forms';
import { zodResolver } from '@primevue/forms/resolvers/zod';
import { z } from 'zod';
import Toast from 'primevue/toast';
import { reactive } from 'vue';
import { z } from 'zod';
import { client } from '@/api/client';
import { useAuthStore } from '@/stores/auth';
import { useToast } from "primevue/usetoast";
import { client } from '@/api/client';
const auth = useAuthStore();
const toast = useToast();

View File

@@ -1,25 +1,25 @@
<template>
<div class=":uno: w-full max-w-md shadow-xl bg-white p-8 rounded-xl m-auto relative before:(content-[''] absolute inset-[-5px] translate-0 z-[-1] opacity-50 rounded-xl bg-[linear-gradient(135deg,var(--glow-stop-1)_0,var(--glow-stop-2)_25%,var(--glow-stop-3)_50%,var(--glow-stop-4)_75%,var(--glow-stop-5)_100%)] animate-[glow-enter-blur_1s_ease_.5s_both]) after:(content-[''] absolute inset-[-1px] translate-0 z-[-1] opacity-50 rounded-xl
bg-[linear-gradient(135deg,transparent_0,transparent_34%,transparent_49%,#fff_57%,#fff_64%,var(--glow-stop-1)_66%,var(--glow-stop-2)_75%,var(--glow-stop-3)_83%,var(--glow-stop-4)_92%,var(--glow-stop-5)_100%)] bg-[length:300%_300%] bg-[position:0_0] bg-no-repeat
transition-background-position duration-800 ease animate-[glow-enter-stroke_.5s_ease_.5s_both])">
<div class="text-center mb-8">
<router-link to="/" class="inline-flex items-center justify-center w-12 h-12 mb-4">
<img class="w-12 h-12" src="/apple-touch-icon.png" alt="Logo" />
</router-link>
<h2 class="text-2xl font-bold text-gray-900">
{{ content[route.name as keyof typeof content]?.title || '' }}
</h2>
<p class="text-gray-500 text-sm mt-1">
{{ content[route.name as keyof typeof content]?.subtitle || '' }}
</p>
<vue-head :input="{
title: content[route.name as keyof typeof content]?.headTitle || 'Authentication',
meta: [
{ name: 'description', content: content[route.name as keyof typeof content]?.subtitle || '' }
]
}" />
<div class="mx-a max-w-md w-full min-h-screen flex flex-col items-center px-4 justify-center">
<div class="w-full h-6 mb-10"></div>
<div
class=":uno: w-full shadow-xl bg-white p-6 rounded-xl relative before:(content-[''] absolute inset-[-5px] translate-0 z-[-1] opacity-50 rounded-xl bg-[linear-gradient(135deg,var(--glow-stop-1)_0,var(--glow-stop-2)_25%,var(--glow-stop-3)_50%,var(--glow-stop-4)_75%,var(--glow-stop-5)_100%)] animate-[glow-enter-blur_1s_ease_.5s_both]) after:(content-[''] absolute inset-[-1px] translate-0 z-[-1] opacity-50 rounded-xl bg-[linear-gradient(135deg,transparent_0,transparent_34%,transparent_49%,#fff_57%,#fff_64%,var(--glow-stop-1)_66%,var(--glow-stop-2)_75%,var(--glow-stop-3)_83%,var(--glow-stop-4)_92%,var(--glow-stop-5)_100%)] bg-[length:300%_300%] bg-[position:0_0] bg-no-repeat transition-background-position duration-800 ease animate-[glow-enter-stroke_.5s_ease_.5s_both])">
<div class="mb-6">
<h2 class="text-xl font-medium text-gray-900">
{{ content[route.name as keyof typeof content]?.title || '' }}
</h2>
<vue-head :input="{
title: content[route.name as keyof typeof content]?.headTitle || 'Authentication',
meta: [
{ name: 'description', content: content[route.name as keyof typeof content]?.subtitle || '' }
]
}" />
</div>
<router-view />
</div>
<router-view />
<router-link to="/" class="inline-flex items-center justify-center w-6 h-6 mt-10 group w-full">
<img class="w-6 h-6" src="/apple-touch-icon.png" alt="Logo" />&ensp;<span
class="text-[#6a6a6a] font-medium group-hover:text-gray-900">EcoStream</span>
</router-link>
</div>
</template>
<script setup lang="ts">
@@ -29,7 +29,7 @@ const route = useRoute();
const content = {
login: {
headTitle: "Login to your account",
title: 'Welcome back',
title: 'Sign in to your dashboard',
subtitle: 'Please enter your details to sign in.'
},
signup: {

View File

@@ -5,7 +5,7 @@
class="flex flex-col gap-4 w-full">
<div class="flex flex-col gap-1">
<label for="email" class="text-sm font-medium text-gray-700">Email</label>
<InputText name="email" type="text" placeholder="user@example.com" fluid
<InputText size="small" name="email" type="text" placeholder="Enter your email" fluid
:disabled="auth.loading" />
<Message v-if="$form.email?.invalid" severity="error" size="small" variant="simple">{{
$form.email.error?.message }}</Message>
@@ -13,27 +13,28 @@
<div class="flex flex-col gap-1">
<label for="password" class="text-sm font-medium text-gray-700">Password</label>
<Password name="password" placeholder="••••••••" :feedback="false" toggleMask fluid
:inputStyle="{ width: '100%' }" :disabled="auth.loading" />
<Password name="password" size="small" placeholder="Enter your password" :feedback="false" toggleMask
fluid :inputStyle="{ width: '100%' }" :disabled="auth.loading" />
<Message v-if="$form.password?.invalid" severity="error" size="small" variant="simple">{{
$form.password.error?.message }}</Message>
</div>
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<Checkbox inputId="remember-me" name="rememberMe" binary :disabled="auth.loading" />
<Checkbox inputId="remember-me" size="small" name="rememberMe" binary :disabled="auth.loading" />
<label for="remember-me" class="text-sm text-gray-900">Remember me</label>
</div>
<div class="text-sm">
<router-link to="/forgot"
class="font-medium text-blue-600 hover:text-blue-500 hover:underline">Forgot
class="text-blue-600 hover:text-blue-500 hover:underline">Forgot
password?</router-link>
</div>
</div>
<Button type="submit" :label="auth.loading ? 'Signing in...' : 'Sign in'" fluid :loading="auth.loading" />
<Button type="submit" size="small" :label="auth.loading ? 'Signing in...' : 'Sign in'" fluid
:loading="auth.loading" />
<div class="relative my-4">
<div class="relative">
<div class="absolute inset-0 flex items-center">
<div class="w-full border-t border-gray-300"></div>
</div>
@@ -42,7 +43,7 @@
</div>
</div>
<Button type="button" variant="outlined" severity="secondary"
<Button size="small" type="button" variant="outlined" severity="secondary"
class="w-full flex items-center justify-center gap-2" @click="loginWithGoogle" :disabled="auth.loading">
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
<path
@@ -50,24 +51,25 @@
</svg>
Google
</Button>
<p class="mt-4 text-center text-sm text-gray-600">
Don't have an account?
<router-link to="/sign-up" class="font-medium text-blue-600 hover:text-blue-500 hover:underline">Sign up
for free</router-link>
</p>
<div class="mt-2 flex flex-col items-center justify-center gap-1 text-sm text-gray-600">
<p class="text-center text-sm text-gray-600">
Don't have an account?
<router-link to="/sign-up" class="font-medium text-blue-600 hover:text-blue-500 hover:underline">Sign up</router-link>
</p>
<!-- <router-link to="/forgot" class="text-blue-600 hover:text-blue-500 hover:underline">Forgot password?</router-link> -->
</div>
</Form>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
import { useAuthStore } from '@/stores/auth';
import { Form, type FormSubmitEvent } from '@primevue/forms';
import { zodResolver } from '@primevue/forms/resolvers/zod';
import { z } from 'zod';
import { useAuthStore } from '@/stores/auth';
import Toast from 'primevue/toast';
import { useToast } from "primevue/usetoast";
import { reactive } from 'vue';
import { z } from 'zod';
const t = useToast();
const auth = useAuthStore();
// const $form = Form.useFormContext();

View File

@@ -4,28 +4,28 @@
class="flex flex-col gap-4 w-full">
<div class="flex flex-col gap-1">
<label for="name" class="text-sm font-medium text-gray-700">Full Name</label>
<InputText name="name" placeholder="John Doe" fluid />
<InputText size="small" name="name" placeholder="John Doe" fluid />
<Message v-if="$form.name?.invalid" severity="error" size="small" variant="simple">{{
$form.name.error?.message }}</Message>
</div>
<div class="flex flex-col gap-1">
<label for="email" class="text-sm font-medium text-gray-700">Email address</label>
<InputText name="email" type="email" placeholder="you@example.com" fluid />
<InputText size="small" name="email" type="email" placeholder="you@example.com" fluid />
<Message v-if="$form.email?.invalid" severity="error" size="small" variant="simple">{{
$form.email.error?.message }}</Message>
</div>
<div class="flex flex-col gap-1">
<label for="password" class="text-sm font-medium text-gray-700">Password</label>
<Password name="password" placeholder="Create a password" :feedback="true" toggleMask fluid
<Password name="password" size="small" placeholder="Create a password" :feedback="true" toggleMask fluid
:inputStyle="{ width: '100%' }" />
<small class="text-gray-500">Must be at least 8 characters.</small>
<Message v-if="$form.password?.invalid" severity="error" size="small" variant="simple">{{
$form.password.error?.message }}</Message>
</div>
<Button type="submit" label="Create Account" fluid />
<Button type="submit" size="small" label="Create Account" fluid />
<p class="mt-4 text-center text-sm text-gray-600">
Already have an account?
@@ -38,9 +38,9 @@
<script setup lang="ts">
import { reactive } from 'vue';
import { Form, type FormSubmitEvent } from '@primevue/forms';
import { zodResolver } from '@primevue/forms/resolvers/zod';
import { reactive } from 'vue';
import { z } from 'zod';

View File

@@ -46,6 +46,7 @@ const quickActions = [
<template>
<div v-if="loading" class="mb-8">
<Skeleton width="10rem" height="1.5rem" class="mb-4"></Skeleton>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div v-for="i in 4" :key="i" class="p-6 rounded-xl border border-gray-200">
<Skeleton shape="circle" size="3rem" class="mb-4"></Skeleton>
@@ -53,6 +54,12 @@ const quickActions = [
<Skeleton width="100%" height="1rem"></Skeleton>
</div>
</div>
<div class="flex flex-col justify-between p-6 rounded-xl border border-gray-200">
<Skeleton width="10rem" height="2rem"></Skeleton>
<Skeleton width="100%" height="1.25rem" class="my-4"></Skeleton>
<Skeleton width="100%" height="1rem"></Skeleton>
</div>
</div>
</div>
<div v-else class="mb-8">
@@ -63,7 +70,6 @@ const quickActions = [
'p-6 rounded-xl text-left transition-all duration-200 flex flex-col bg-white',
'border border-gray-300 hover:border-primary hover:shadow-lg',
'group press-animated',
]">
<div
:class="['w-12 h-12 rounded-lg flex items-center justify-center mb-4 bg-gray-100 group-hover:bg-primary/10']">

View File

@@ -1,7 +1,7 @@
<template>
<div class="rounded-xl border border-gray-300 hover:border-primary hover:shadow-lg text-card-foreground bg-white">
<div class="flex flex-col space-y-1.5 p-6">
<h3 class="text-2xl font-semibold leading-none tracking-tight">Referral Link</h3>
<h3 class="text-lg font-semibold leading-none tracking-tight">Referral Link</h3>
</div>
<div class="p-6 pt-0 space-y-4">
<p class="text-sm text-gray-600 font-medium">Share your referral link and earn commissions from

View File

@@ -25,7 +25,7 @@ defineProps<Props>();
<Skeleton width="5rem" height="1rem" class="mb-2"></Skeleton>
<Skeleton width="8rem" height="2rem"></Skeleton>
</div>
<Skeleton shape="circle" size="3rem"></Skeleton>
<!-- <Skeleton shape="circle" size="3rem"></Skeleton> -->
</div>
<Skeleton width="4rem" height="1rem"></Skeleton>
</div>

View File

@@ -13,10 +13,10 @@ defineEmits<{
</script>
<template>
<div class="lg:col-span-2 relative overflow-hidden rounded-2xl bg-gradient-to-br from-gray-900 to-gray-800 text-white p-8 shadow-xl">
<div class=":uno: lg:col-span-2 relative overflow-hidden rounded-2xl bg-gradient-to-br from-gray-900 to-gray-800 text-white p-8">
<!-- Background decorations -->
<div class="absolute top-0 right-0 -mt-16 -mr-16 w-64 h-64 bg-primary-500 rounded-full mix-blend-overlay filter blur-3xl opacity-20"></div>
<div class="absolute bottom-0 left-0 -mb-16 -ml-16 w-64 h-64 bg-purple-500 rounded-full mix-blend-overlay filter blur-3xl opacity-20"></div>
<div class="absolute top-0 right-0 -mt-16 -mr-16 w-64 h-64 bg-primary-500 rounded-full blur-3xl opacity-20"></div>
<div class="absolute bottom-0 left-0 -mb-16 -ml-16 w-64 h-64 bg-purple-500 rounded-full blur-3xl opacity-20"></div>
<div class="relative z-10 flex flex-col h-full justify-between">
<div class="flex justify-between items-start">

View File

@@ -1,8 +1,8 @@
<script setup lang="ts">
import type { ModelUser } from '@/api/client';
import Avatar from 'primevue/avatar';
import Tag from 'primevue/tag';
import Button from 'primevue/button';
import Tag from 'primevue/tag';
import { computed } from 'vue';
const props = defineProps<{
@@ -24,10 +24,10 @@ const joinDate = computed(() => {
</script>
<template>
<div class="relative overflow-hidden rounded-2xl bg-gradient-to-r from-gray-900 via-gray-800 to-gray-900 text-white p-8 md:p-10 shadow-xl">
<div class="relative overflow-hidden rounded-2xl bg-gradient-to-r from-gray-900 via-gray-800 to-gray-900 text-white p-8 md:p-10">
<!-- Background decorations -->
<div class="absolute top-0 right-0 -mt-20 -mr-20 w-80 h-80 bg-primary-500 rounded-full mix-blend-overlay filter blur-3xl opacity-20"></div>
<div class="absolute bottom-0 left-0 -mb-20 -ml-20 w-80 h-80 bg-purple-500 rounded-full mix-blend-overlay filter blur-3xl opacity-20"></div>
<div class="absolute top-0 right-0 -mt-20 -mr-20 w-80 h-80 bg-primary-500 rounded-full mix-blend-overlay filter blur-3xl"></div>
<div class="absolute bottom-0 left-0 -mb-20 -ml-20 w-80 h-80 bg-purple-500 rounded-full mix-blend-overlay filter blur-3xl"></div>
<div class="relative z-10 flex flex-col md:flex-row items-center gap-8">
<div class="relative">
@@ -60,7 +60,7 @@ const joinDate = computed(() => {
</div>
<div class="flex gap-3">
<Button label="Logout" severity="secondary" class="border-white/10 text-white hover:bg-white/10 bg-white/5" @click="emit('logout')">
<Button label="Logout" severity="danger" class="border-white/10 text-white hover:bg-white/10 bg-white/5" @click="emit('logout')">
<template #icon>
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/>

View File

@@ -202,7 +202,7 @@ export default defineConfig({
getCSS: (context) => {
return `
:root {
--font-sans: 'Be Vietnam Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
--font-sans: 'Google Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
--font-serif: 'Playfair Display', serif, 'Times New Roman', Times, serif;
--glow-stop-1: #ffc400;
--glow-stop-2: #ff9100;