8000 feature: add profile page and modify ui · code4mk/sveltekit-admin@4cc85a8 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4cc85a8

Browse files
committed
feature: add profile page and modify ui
1 parent 1d24535 commit 4cc85a8

File tree

8 files changed

+189
-6
lines changed

8 files changed

+189
-6
lines changed

src/lib/components/Header.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
const profileMenu = createMenu({ label: 'Profile' });
1616
1717
const menuItems = [
18-
{ text: 'Your profile', action: () => goto('/profile') },
18+
{ text: 'Your profile', action: () => goto('/admin/profile') },
1919
{ text: 'Sign out', action: () => goto('/logout') }
2020
];
2121
@@ -30,7 +30,7 @@
3030
<div class="flex justify-between items-center h-16">
3131
<div class="flex items-center pl-4">
3232
<button
33-
class="p-2 mr-2 text-gray-400 hover:text-gray-500"
33+
class="p-2 mr-2 text-gray-400 hover:text-gray-500 cursor-pointer"
3434
on:click={() => $sidebarOpen = !$sidebarOpen}
3535
>
3636
{#if $sidebarOpen}

src/lib/components/Sidebar.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
{#each menuItems as item}
3434
<a
3535
href={item.href}
36-
class="group flex items-center px-2 py-2 text-sm font-medium rounded-md {$page.url.pathname === item.href ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'}"
36+
class="group flex items-center px-2 py-2 text-sm font-medium rounded-md mt-1 {$page.url.pathname === item.href ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'}"
3737
>
3838
<svelte:component
3939
this={item.icon}

src/routes/+page.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<h1>Welcome to SvelteKit</h1>
33
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>
44
<nav class="mt-8">
5+
<a href="/blog" class="inline-block px-4 py-2 bg-blue-600 text-white no-underline rounded hover:bg-blue-700 transition-colors">Blog</a>
56
<a href="/about" class="inline-block px-4 py-2 bg-blue-600 text-white no-underline rounded hover:bg-blue-700 transition-colors">Go to About Page →</a>
67
<a href="/admin" class="inline-block px-4 py-2 bg-gray-600 text-white no-underline rounded hover:bg-gray-700 transition-colors ml-4">Admin →</a>
78
</nav>

src/routes/admin/customers/+page.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
<div class="mt-8 flex flex-col">
5757
<div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
5858
<div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
59-
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
59+
<div class="overflow-hidden shadow ring-1 ring-gray-300 ring-opacity-5 md:rounded-lg">
6060
<table class="min-w-full divide-y divide-gray-300">
6161
<thead class="bg-gray-50">
6262
<tr>

src/routes/admin/orders/+page.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
<div class="mt-8 flex flex-col">
6161
<div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
6262
<div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
63-
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
63+
<div class="overflow-hidden shadow ring-1 ring-gray-300 ring-opacity-5 md:rounded-lg">
6464
<table class="min-w-full divide-y divide-gray-300">
6565
<thead class="bg-gray-50">
6666
<tr>

src/routes/admin/products/+page.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
<div class="mt-8 flex flex-col">
5959
<div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
6060
<div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
61-
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
61+
<div class="overflow F438 -hidden shadow ring-1 ring-gray-300 ring-opacity-5 md:rounded-lg">
6262
<table class="min-w-full divide-y divide-gray-300">
6363
<thead class="bg-gray-50">
6464
<tr>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { superValidate } from 'sveltekit-superforms/server';
2+
import { zod } from 'sveltekit-superforms/adapters';
3+
import { z } from 'zod';
4+
import { fail } from '@sveltejs/kit';
5+
6+
const profileSchema = z.object({
7+
name: z.string().min(1, "Full name is required"),
8+
email: z.string().email("Invalid email address"),
9+
phone: z.string().min(1, "Phone number is required"),
10+
location: z.string().min(1, "Location is required"),
11+
profileImage: z.string().optional()
12+
});
13+
14+
export const load = async () => {
15+
// Pre-fill with dummy data
16+
const form = await superValidate({
17+
name: "John Doe",
18+
email: "john@example.com",
19+
phone: "+1 (555) 000-0000",
20+
location: "New York, USA"
21+
}, zod(profileSchema));
22+
23+
return { form };
24+
};
25+
26+
export const actions = {
27+
default: async ({ request }) => {
28+
const form = await superValidate(request, zod(profileSchema));
29+
30+
if (!form.valid) {
31+
return fail(400, { form });
32+
}
33+
34+
// Add your profile update logic here
35+
console.log('Profile data:', form.data);
36+
37+
return { form };
38+
}
39+
};

src/routes/admin/profile/+page.svelte

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<script lang="ts">
2+
import AdminLayout from '$lib/components/AdminLayout.svelte';
3+
import { User, Mail, Phone, MapPin, Camera } from '@lucide/svelte';
4+
import { superForm } from 'sveltekit-superforms';
5+
import { zod } from 'sveltekit-superforms/adapters';
6+
import toast from 'svelte-french-toast';
7+
8+
export let data;
9+
10+
const { form, errors, enhance } = superForm(data.form, {
11+
onUpdated: ({ form }) => {
12+
if (form.valid) {
13+
toast.success('Profile updated successfully');
14+
}
15+
}
16+
});
17+
18+
let profileImage: string | null = null;
19+
20+
function handleImageUpload(event: Event) {
21+
const target = event.target as HTMLInputElement;
22+
if (target.files && target.files[0]) {
23+
const reader = new FileReader();
24+
reader.onload = (e) => {
25+
profileImage = e.target?.result as string;
26+
};
27+
reader.readAsDataURL(target.files[0]);
28+
}
29+
}
30+
</script>
31+
32+
<AdminLayout>
33+
<div class="max-w-4xl mx-auto px-4 py-8">
34+
<h1 class="text-2xl font-semibold text-gray-900">Profile Settings</h1>
35+
<p class="mt-2 text-sm text-gray-700">Manage your account settings and preferences.</p>
36+
37+
<form class="mt-8 space-y-8" use:enhance>
38+
<!-- Profile Photo -->
39+
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
40+
<h2 class="text-lg font-medium text-gray-900 mb-6">Profile Photo</h2>
41+
<div class="flex items-center">
42+
<div class="relative">
43+
<div class="w-24 h-24 rounded-full bg-gray-200 flex items-center justify-center overflow-hidden">
44+
{#if profileImage}
45+
<img src={profileImage} alt="Profile" class="w-full h-full object-cover" />
46+
{:else}
47+
<User class="w-12 h-12 text-gray-400" />
48+
{/if}
49+
</div>
50+
<label class="absolute bottom-0 right-0 bg-indigo-600 rounded-full p-2 cursor-pointer hover:bg-indigo-700">
51+
<Camera class="w-4 h-4 text-white" />
52+
<input type="file" class="hidden" accept="image/*" on:change={handleImageUpload} />
53+
</label>
54+
</div>
55+
<div class="ml-6">
56+
<p class="text-sm font-medium text-gray-900">Upload new photo</p>
57+
<p class="text-sm text-gray-500 mt-1">JPG, GIF or PNG. Max size of 2MB</p>
58+
</div>
59+
</div>
60+
</div>
61+
62+
<!-- Personal Information -->
63+
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
64+
<h2 class="text-lg font-medium text-gray-900 mb-6">Personal Information</h2>
65+
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
66+
<div>
67+
<label class="block text-sm font-medium text-gray-700 mb-2">Full Name</label>
68+
<div class="relative">
69+
<User class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
70+
<input
71+
type="text"
72+
bind:value={$form.name}
73+
class="w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-200 focus:ring-2 focus:ring-indigo-500/20 focus:border-indigo-500"
74+
placeholder="John Doe"
75+
/>
76+
</div>
77+
{#if $errors.name}
78+
<p class="mt-1.5 text-sm text-red-500">{$errors.name}</p>
79+
{/if}
80+
</div>
81+
82+
<div>
83+
<label class="block text-sm font-medium text-gray-700 mb-2">Email</label>
84+
<div class="relative">
85+
<Mail class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
86+
<input
87+
type="email"
88+
bind:value={$form.email}
89+
class="w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-200 focus:ring-2 focus:ring-indigo-500/20 focus:border-indigo-500"
90+
placeholder="john@example.com"
91+
/>
92+
</div>
93+
{#if $errors.email}
94+
<p class="mt-1.5 text-sm text-red-500">{$errors.email}</p>
95+
{/if}
96+
</div>
97+
98+
<div>
99+
<label class="block text-sm font-medium text-gray-700 mb-2">Phone</label>
100+
<div class="relative">
101+
<Phone class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
102+
<input
103+
type="tel"
104+
bind:value={$form.phone}
105+
class="w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-200 focus:ring-2 focus:ring-indigo-500/20 focus:border-indigo-500"
106+
placeholder="+1 (555) 000-0000"
107+
/>
108+
</div>
109+
{#if $errors.phone}
110+
<p class="mt-1.5 text-sm text-red-500">{$errors.phone}</p>
111+
{/if}
112+
</div>
113+
114+
<div>
115+
<label class="block text-sm font-medium text-gray-700 mb-2">Location</label>
116+
<div class="relative">
117+
<MapPin class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
118+
<input
119+
type="text"
120+
bind:value={$form.location}
121+
class="w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-200 focus:ring-2 focus:ring-indigo-500/20 focus:border-indigo-500"
122+
placeholder="New York, USA"
123+
/>
124+
</div>
125+
{#if $errors.location}
126+
<p class="mt-1.5 text-sm text-red-500">{$errors.location}</p>
127+
{/if}
128+
</div>
129+
</div>
130+
</div>
131+
132+
<!-- Submit Button -->
133+
<div class="flex justify-end">
134+
<button
135+
type="submit"
136+
class="px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-lg hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
137+
>
138+
Save Changes
139+
</button>
140+
</div>
141+
</form>
142+
</div>
143+
</AdminLayout>

0 commit comments

Comments
 (0)
0