- Add adminApi with full CRUD for users, companies, listings, oil-prices - Add statsApi for fetching latest market price aggregates - Add AdminTable component and /admin page for site management - Add StatsPrice, UpdateUserRequest, UpdateOilPriceRequest types - Add url field support in listing create/edit forms - Update state SVG data for all 6 New England states - Update county page to display richer listing info (phone, url, bio%) - Misc layout and style updates across vendor and public routes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
105 lines
4.2 KiB
Svelte
Executable File
105 lines
4.2 KiB
Svelte
Executable File
<script lang="ts">
|
|
import { onMount } from "svelte";
|
|
import { writable } from "svelte/store";
|
|
import type { Writable } from 'svelte/store';
|
|
import '../../app.postcss'; // Import Tailwind CSS
|
|
import { user, darkMode, type User } from '$lib/states';
|
|
import { authApi } from '$lib/api';
|
|
|
|
// Initialize dark mode on mount to ensure data-theme is set
|
|
onMount(() => {
|
|
const isDark = $darkMode;
|
|
if (isDark) {
|
|
document.documentElement.setAttribute('data-theme', 'dark');
|
|
} else {
|
|
document.documentElement.setAttribute('data-theme', 'light');
|
|
}
|
|
});
|
|
|
|
// Placeholder for user store - in a real app, this would be managed by an auth library or context
|
|
let storedUser: User | null = null;
|
|
|
|
// Check for user session on mount
|
|
onMount(() => {
|
|
const storedUserString = localStorage.getItem('user');
|
|
if (storedUserString) {
|
|
storedUser = JSON.parse(storedUserString);
|
|
user.set(storedUser);
|
|
} else {
|
|
// No user stored
|
|
localStorage.removeItem('user');
|
|
user.set(null);
|
|
}
|
|
});
|
|
|
|
user.subscribe((newUser) => {
|
|
if (newUser) {
|
|
storedUser = newUser;
|
|
} else {
|
|
storedUser = null;
|
|
}
|
|
});
|
|
|
|
// Logout function - now async to call API to clear httpOnly cookie
|
|
const logout = async () => {
|
|
await authApi.logout();
|
|
user.set(null);
|
|
window.location.href = '/';
|
|
};
|
|
|
|
// Toggle dark mode
|
|
const toggleDarkMode = () => {
|
|
darkMode.update((value: boolean) => !value);
|
|
};
|
|
</script>
|
|
|
|
<div class="min-h-screen flex flex-col">
|
|
<header class="navbar bg-primary text-primary-content shadow-lg">
|
|
<div class="flex-1">
|
|
<a href="/" class="btn btn-ghost normal-case text-xl">Biz Hero</a>
|
|
</div>
|
|
<div class="flex-none flex items-center gap-2">
|
|
<button type="button" class="btn btn-ghost" on:click={toggleDarkMode}>
|
|
{#if !$darkMode}
|
|
<!-- Moon icon for dark mode -->
|
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
<path fill-rule="evenodd" d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" clip-rule="evenodd" />
|
|
</svg>
|
|
{:else}
|
|
<!-- Sun icon for light mode -->
|
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
<path fill-rule="evenodd" d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" clip-rule="evenodd" />
|
|
</svg>
|
|
{/if}
|
|
</button>
|
|
{#if $user}
|
|
<div class="dropdown dropdown-end">
|
|
<button type="button" class="btn btn-ghost normal-case text-lg">
|
|
{$user.username}
|
|
</button>
|
|
<ul class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-52 text-black dark:text-gray-200">
|
|
<li><a href="/vendor">Dashboard</a></li>
|
|
{#if $user.username === 'Anekdotin'}
|
|
<li><a href="/admin">Admin Dashboard</a></li>
|
|
{/if}
|
|
<li><button type="button" on:click={logout}>Logout</button></li>
|
|
</ul>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</header>
|
|
|
|
<main class="flex-grow container mx-auto p-4">
|
|
<slot />
|
|
</main>
|
|
|
|
<footer class="footer footer-center p-4 bg-base-300 text-base-content">
|
|
<div>
|
|
<p>Copyright © {new Date().getFullYear()} - All right reserved</p>
|
|
{#if !$user}
|
|
<a href="/login" class="link link-primary">Vendor Login</a>
|
|
{/if}
|
|
</div>
|
|
</footer>
|
|
</div>
|