Major UI update:

Improved dark mode
Added company banner and moved change company
Changed campaign trends, and now requires 2 campaigns atleast
Menu collapses if open and link clicked
Changed calendar event type indicator
Changed mobile menu
Changed campaign event timeline and event cards
Various UI changes

Signed-off-by: Ronni Skansing <rskansing@gmail.com>
This commit is contained in:
Ronni Skansing
2025-10-12 13:46:15 +02:00
parent a38132da6e
commit 0b3b796a00
53 changed files with 1645 additions and 807 deletions
+5 -7
View File
@@ -243,9 +243,7 @@
</script>
{#if visible}
<div
class="fixed top-0 left-0 w-full h-full bg-cta-blue dark:bg-gray-900 opacity-20 blur-xl transition-colors duration-200"
/>
<div class="fixed top-0 left-0 w-full h-full opacity-[0.5]" />
<div
class="fixed top-0 left-0 w-full h-full flex justify-center items-center backdrop-blur-sm z-20"
role="dialog"
@@ -259,7 +257,7 @@
>
<!-- Header -->
<div
class="bg-red-700 dark:bg-red-800 text-white rounded-t-md py-4 px-8 flex items-center justify-between flex-shrink-0 transition-colors duration-200"
class="bg-red-700 dark:bg-red-700/90 text-white rounded-t-md py-4 px-8 flex items-center justify-between flex-shrink-0 transition-colors duration-200"
>
<div class="flex items-center">
<svg
@@ -293,7 +291,7 @@
<div class="px-8 py-6">
<div
id="alert-description"
class="text-gray-600 dark:text-gray-300 transition-colors duration-200"
class="text-gray-600 dark:text-gray-400 transition-colors duration-200"
>
{#if $$slots.default}
<slot />
@@ -317,13 +315,13 @@
<!-- Footer -->
<div
class="py-4 row-span-2 col-start-1 col-span-3 border-t-2 border-gray-200 dark:border-gray-600 w-full flex flex-row justify-center items-center sm:justify-center md:justify-center lg:justify-end xl:justify-end 2xl:justify-end px-8 bg-gray-50 dark:bg-gray-700 rounded-b-md transition-colors duration-200"
class="py-4 row-span-2 col-start-1 col-span-3 border-t-2 border-gray-200 dark:border-gray-700/60 w-full flex flex-row justify-center items-center sm:justify-center md:justify-center lg:justify-end xl:justify-end 2xl:justify-end px-8 bg-gray-50 dark:bg-gray-800/40 rounded-b-md transition-colors duration-200"
>
{#if !noCancel}
<button
type="reset"
on:click={close}
class="bg-slate-400 hover:bg-slate-300 dark:bg-gray-600 dark:hover:bg-gray-500 text-sm mr-2 uppercase font-bold px-4 py-2 text-white rounded-md transition-colors duration-200"
class="bg-slate-400 hover:bg-slate-300 dark:bg-gray-700/80 dark:hover:bg-gray-600/80 text-sm mr-2 uppercase font-bold px-4 py-2 text-white rounded-md transition-colors duration-200"
>
{cancel}
</button>
@@ -46,7 +46,7 @@
SCHEDULED: '#62aded', // campaign-scheduled
ACTIVE: '#5557f6', // campaign-active
COMPLETED: '#4cb5b5', // message-read - much more muted than bright green
SELF_MANAGED: '#9F7AEA' // purple
SELF_MANAGED: '#7C3AED' // darker purple
};
function sortCampaignsByPriority(campaigns, day) {
@@ -308,19 +308,19 @@
</script>
<div
class="w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm p-4 border border-gray-200 dark:border-gray-700 transition-colors duration-200"
class="w-full bg-white dark:bg-gray-900/80 rounded-lg shadow-sm p-4 border border-gray-200 dark:border-gray-700/60 transition-colors duration-200"
>
<div class="space-y-4 max-w-5xl mx-auto min-h-[600px]">
<!-- Navigation Controls -->
<div class="flex justify-center items-center">
<button
class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 mx-4 transition-colors duration-200"
class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-800/60 mx-4 transition-colors duration-200"
on:click={previousMonth}
disabled={isLoadingNewMonth}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 text-gray-600 dark:text-gray-300"
class="h-5 w-5 text-gray-600 dark:text-gray-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
@@ -334,18 +334,18 @@
</svg>
</button>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">
<h2 class="text-lg font-semibold text-gray-900 dark:text-gray-300">
{format(currentMonth, 'MMMM yyyy')}
</h2>
<button
class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 mx-4 transition-colors duration-200"
class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-800/60 mx-4 transition-colors duration-200"
on:click={nextMonth}
disabled={isLoadingNewMonth}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 text-gray-600 dark:text-gray-300"
class="h-5 w-5 text-gray-600 dark:text-gray-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
@@ -367,7 +367,7 @@
style="background-color: {item.color}; opacity: {activeFilters[item.key] ? '1' : '0.3'}"
></div>
<span
class="transition-opacity duration-200 text-gray-700 dark:text-gray-300"
class="transition-opacity duration-200 text-gray-700 dark:text-gray-400"
style="opacity: {activeFilters[item.key] ? '1' : '0.5'}">{item.label}</span
>
</button>
@@ -379,7 +379,7 @@
<!-- Day headers -->
<div class="grid grid-cols-7 text-center mb-1">
{#each ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] as day}
<div class="text-sm font-medium text-gray-600 dark:text-gray-300">{day}</div>
<div class="text-sm font-medium text-gray-600 dark:text-gray-400">{day}</div>
{/each}
</div>
@@ -388,9 +388,9 @@
<div class="min-h-[450px] flex items-center justify-center">
<div class="flex items-center space-x-2">
<div
class="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600 dark:border-blue-400"
class="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600 dark:border-highlight-blue/80"
></div>
<span class="text-gray-600 dark:text-gray-300">Loading calendar...</span>
<span class="text-gray-600 dark:text-gray-400">Loading calendar...</span>
</div>
</div>
{:else}
@@ -400,19 +400,19 @@
{#each week as day}
<div
class="calendar-day relative border rounded-md overflow-hidden {scrollBarClassesHorizontal} {day.isToday
? 'border-gray-700 dark:border-gray-400 bg-gray-50 dark:bg-gray-700'
: 'border-gray-200 dark:border-gray-600'}
? 'border-gray-700 dark:border-gray-500 bg-gray-50 dark:bg-gray-800/60'
: 'border-gray-200 dark:border-gray-700/60'}
{day.isCurrentMonth
? 'bg-white dark:bg-gray-800'
: 'bg-gray-50 dark:bg-gray-700'} transition-colors duration-200"
? 'bg-white dark:bg-gray-900/60'
: 'bg-gray-50 dark:bg-gray-800/40'} transition-colors duration-200"
>
<!-- Date number -->
<div
class="text-sm p-1 {day.isToday
? 'font-bold text-gray-800 dark:text-white'
? 'font-bold text-gray-800 dark:text-gray-300'
: day.isCurrentMonth
? 'text-gray-600 dark:text-gray-300'
: 'text-gray-400 dark:text-gray-500'}"
? 'text-gray-600 dark:text-gray-400'
: 'text-gray-400 dark:text-gray-600'}"
>
{day.date.getDate()}
</div>
@@ -423,8 +423,8 @@
{#each day.campaigns as campaign}
<a
href={`/campaign/${campaign.id}`}
class="block mb-1 rounded-sm text-white text-xs p-1 overflow-hidden hover:opacity-90 transition-opacity"
style="background-color: {campaign.color};"
class="block mb-1 rounded-sm text-xs p-1 pl-2 overflow-hidden hover:bg-gray-200 dark:hover:bg-gray-700/40 transition-all duration-200 bg-gray-100 dark:bg-gray-800/30"
style="border-left: 3px solid {campaign.color};"
title="{campaign.name} - {campaign.isSelfManaged
? 'Self-managed'
: campaign.end < new Date()
@@ -435,7 +435,7 @@
? `${formatDateString(new Date(campaign.createdAt))} - ?`
: `${formatDateString(campaign.start)} - ${formatDateString(campaign.end)}`}"
>
<div class="campaign-name truncate text-white">
<div class="campaign-name truncate text-gray-700 dark:text-gray-300">
{truncateText(campaign.name, 18)}
</div>
</a>
@@ -2,6 +2,21 @@
import { onMount, onDestroy, tick } from 'svelte';
let resizeObserver;
// Theme detection for chart reactivity
let isDarkMode = false;
// Check theme on mount and when it changes
const checkTheme = () => {
const newIsDarkMode = document.documentElement.classList.contains('dark');
if (newIsDarkMode !== isDarkMode) {
isDarkMode = newIsDarkMode;
// Recreate chart when theme changes
if (containerReady && chartData.length >= 2) {
tick().then(() => createChart());
}
}
};
export let campaignStats = [];
export let isLoading = false;
@@ -238,6 +253,16 @@
svg.setAttribute('class', 'campaign-trend-chart');
chartContainer.appendChild(svg);
// Add dark background rectangle
const bgRect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
bgRect.setAttribute('width', '100%');
bgRect.setAttribute('height', '100%');
bgRect.setAttribute(
'fill',
document.documentElement.classList.contains('dark') ? '#111827' : '#ffffff'
);
svg.appendChild(bgRect);
const xExtent = [0, chartData.length - 1];
// Map 0-100% directly to chart boundaries so 0% aligns with X-axis
const yExtent = [0, 100];
@@ -346,7 +371,7 @@
stop1.setAttribute('offset', '0%');
stop1.setAttribute(
'stop-color',
document.documentElement.classList.contains('dark') ? '#374151' : '#f8f9fa'
document.documentElement.classList.contains('dark') ? '#1f2937' : '#f8f9fa'
);
stop1.setAttribute('stop-opacity', '0.3');
@@ -354,15 +379,15 @@
stop2.setAttribute('offset', '50%');
stop2.setAttribute(
'stop-color',
document.documentElement.classList.contains('dark') ? '#4b5563' : '#e9ecef'
document.documentElement.classList.contains('dark') ? '#374151' : '#e9ecef'
);
stop2.setAttribute('stop-opacity', '0.5');
stop2.setAttribute('stop-opacity', '0.4');
const stop3 = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
stop3.setAttribute('offset', '100%');
stop3.setAttribute(
'stop-color',
document.documentElement.classList.contains('dark') ? '#374151' : '#f8f9fa'
document.documentElement.classList.contains('dark') ? '#1f2937' : '#f8f9fa'
);
stop3.setAttribute('stop-opacity', '0.3');
@@ -400,7 +425,7 @@
vLine.setAttribute('y2', (height - margin.bottom).toString());
vLine.setAttribute(
'stroke',
document.documentElement.classList.contains('dark') ? '#4b5563' : '#e5e7eb'
document.documentElement.classList.contains('dark') ? '#374151' : '#e5e7eb'
);
vLine.setAttribute('stroke-width', '0.5');
vLine.setAttribute('opacity', '0.3');
@@ -416,7 +441,7 @@
yAxis.setAttribute('y2', (height - margin.bottom).toString());
yAxis.setAttribute(
'stroke',
document.documentElement.classList.contains('dark') ? '#9ca3af' : '#6B7280'
document.documentElement.classList.contains('dark') ? '#6887ea' : '#6B7280'
);
yAxis.setAttribute('stroke-width', '2');
svg.appendChild(yAxis);
@@ -428,7 +453,7 @@
xAxis.setAttribute('y2', (height - margin.bottom).toString());
xAxis.setAttribute(
'stroke',
document.documentElement.classList.contains('dark') ? '#9ca3af' : '#6B7280'
document.documentElement.classList.contains('dark') ? '#6887ea' : '#6B7280'
);
xAxis.setAttribute('stroke-width', '2');
svg.appendChild(xAxis);
@@ -443,7 +468,7 @@
text.setAttribute('font-size', '11');
text.setAttribute(
'fill',
document.documentElement.classList.contains('dark') ? '#d1d5db' : '#6B7280'
document.documentElement.classList.contains('dark') ? '#e5e7eb' : '#6B7280'
);
text.setAttribute('font-weight', '500');
text.setAttribute('alignment-baseline', 'middle');
@@ -461,7 +486,7 @@
text.setAttribute('font-size', '11');
text.setAttribute(
'fill',
document.documentElement.classList.contains('dark') ? '#f9fafb' : '#000'
document.documentElement.classList.contains('dark') ? '#f3f4f6' : '#000'
);
text.setAttribute('alignment-baseline', 'middle');
// Show date (YYYY/MM) and campaign name on the same line
@@ -810,8 +835,21 @@
});
}
let themeObserver;
onMount(async () => {
await tick(); // Wait for DOM/layout
// Initialize theme detection
checkTheme();
// Watch for theme changes
themeObserver = new MutationObserver(checkTheme);
themeObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class']
});
if (sizingContainer) {
const containerWidth = sizingContainer.parentElement?.clientWidth || 0;
width = Math.min(Math.max(containerWidth, 300), containerWidth); // Minimum 300px but never exceed container
@@ -833,6 +871,10 @@
if (resizeObserver && sizingContainer) {
resizeObserver.unobserve(sizingContainer.parentElement || sizingContainer);
}
// Clean up theme observer
if (themeObserver) {
themeObserver.disconnect();
}
if (loadingTimeout) {
clearTimeout(loadingTimeout);
}
@@ -856,14 +898,16 @@
></div>
{#if !containerReady}
<div class="flex items-center justify-center min-h-[100px]">
<span class="text-gray-400 text-sm">Preparing chart…</span>
<span class="text-gray-400 dark:text-gray-500 text-sm transition-colors duration-200"
>Preparing chart…</span
>
</div>
{:else}
<div>
{#if debouncedIsLoading}
<div class="flex items-center justify-center h-64">
<div
class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 dark:border-blue-400"
class="animate-spin rounded-full h-8 w-8 border-b-2 border-cta-blue dark:border-highlight-blue transition-colors duration-200"
></div>
<span class="ml-2 text-gray-600 dark:text-gray-300 transition-colors duration-200"
>Loading trend data...</span
@@ -875,109 +919,53 @@
>Preparing trend data...</span
>
</div>
{:else if hasAttemptedLoad && !isLoading && !debouncedIsLoading && chartData.length === 0}
{:else if hasAttemptedLoad && !isLoading && !debouncedIsLoading && chartData.length < 2}
<div
class="flex items-center justify-center h-64 bg-gray-50 dark:bg-gray-800 rounded-lg transition-colors duration-200"
class="bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-700 p-6 transition-colors duration-200"
>
<div class="text-center">
<svg
class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500 transition-colors duration-200"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
<!-- Match the height structure of the data container -->
<div class="flex flex-row items-center justify-between mb-2 pb-0 flex-wrap gap-2">
<h4
class="text-sm font-medium text-gray-600 dark:text-gray-300 m-0 transition-colors duration-200"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
/>
</svg>
<h3
class="mt-2 text-sm font-medium text-gray-900 dark:text-gray-200 transition-colors duration-200"
>
No campaign data
</h3>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400 transition-colors duration-200">
Campaign statistics will appear here once campaigns are completed.
</p>
Trend Analytics
</h4>
</div>
</div>
{:else if hasAttemptedLoad && !isLoading && !debouncedIsLoading && chartData.length === 1}
<div
class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-6 transition-colors duration-200"
>
<div class="flex items-center">
<svg
class="h-6 w-6 text-blue-600 dark:text-blue-400 mr-2 transition-colors duration-200"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<div>
<h4
class="text-sm font-medium text-blue-900 dark:text-blue-200 transition-colors duration-200"
<div class="mb-8"></div>
<!-- Centered content matching stats grid height -->
<div class="flex items-center justify-center min-h-[350px] py-16">
<div class="text-center">
<svg
class="mx-auto h-12 w-12 text-cta-blue dark:text-highlight-blue transition-colors duration-200"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
Single Campaign Data
</h4>
<p class="text-sm text-blue-700 dark:text-blue-300 transition-colors duration-200">
Trends will appear when you have 2 or more completed campaigns.
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
/>
</svg>
<h3
class="mt-3 text-lg font-medium text-gray-900 dark:text-gray-100 transition-colors duration-200"
>
Insufficient data for trend analysis
</h3>
<p
class="mt-2 text-sm text-gray-600 dark:text-gray-400 transition-colors duration-200"
>
Trend analysis requires at least 2 completed campaigns to show meaningful patterns.
</p>
</div>
</div>
<div class="grid grid-cols-4 gap-4">
<div class="text-center">
<div
class="text-2xl font-bold text-blue-600 dark:text-blue-400 transition-colors duration-200"
>
{chartData[0].openRate}%
</div>
<div class="text-sm text-gray-600 dark:text-gray-400 transition-colors duration-200">
Open Rate
</div>
</div>
<div class="text-center">
<div
class="text-2xl font-bold text-green-600 dark:text-green-400 transition-colors duration-200"
>
{chartData[0].clickRate}%
</div>
<div class="text-sm text-gray-600 dark:text-gray-400 transition-colors duration-200">
Click Rate
</div>
</div>
<div class="text-center">
<div
class="text-2xl font-bold text-yellow-600 dark:text-yellow-400 transition-colors duration-200"
>
{chartData[0].submissionRate}%
</div>
<div class="text-sm text-gray-600 dark:text-gray-400 transition-colors duration-200">
Submission Rate
</div>
</div>
<div class="text-center">
<div
class="text-2xl font-bold text-indigo-600 dark:text-indigo-400 transition-colors duration-200"
>
{chartData[0].reportRate}%
</div>
<div class="text-sm text-gray-600 dark:text-gray-400 transition-colors duration-200">
Report Rate
</div>
</div>
</div>
</div>
{:else if hasAttemptedLoad && !isLoading && !debouncedIsLoading && chartData.length >= 2}
<div
class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-600 p-6 transition-colors duration-200"
class="bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-700 p-6 transition-colors duration-200"
>
<!-- Trendline stats and controls above chart -->
<div class="flex flex-row items-center justify-between mb-2 pb-0 flex-wrap gap-2">
@@ -993,7 +981,7 @@
Time range:
<select
bind:value={selectedTimeRange}
class="border border-gray-300 dark:border-gray-600 rounded px-1 py-0 text-xs bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-200 transition-colors duration-200"
class="border border-gray-300 dark:border-gray-600 rounded px-1 py-0 text-xs bg-white dark:bg-gray-900 text-gray-700 dark:text-gray-200 hover:border-cta-blue dark:hover:border-highlight-blue focus:border-cta-blue dark:focus:border-highlight-blue transition-colors duration-200"
style="height: 1.5rem;"
>
{#each timeRanges as range}
@@ -1011,7 +999,7 @@
min="1"
max={chartData.length}
bind:value={trendN}
class="border border-gray-300 dark:border-gray-600 rounded px-1 py-0 w-10 text-xs bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-200 transition-colors duration-200"
class="border border-gray-300 dark:border-gray-600 rounded px-1 py-0 w-10 text-xs bg-white dark:bg-gray-900 text-gray-700 dark:text-gray-200 hover:border-cta-blue dark:hover:border-highlight-blue focus:border-cta-blue dark:focus:border-highlight-blue transition-colors duration-200"
style="height: 1.5rem;"
/>
</label>
@@ -1025,13 +1013,13 @@
min="2"
max={chartData.length}
bind:value={movingAvgN}
class="border border-gray-300 dark:border-gray-600 rounded px-1 py-0 w-10 text-xs bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-200 transition-colors duration-200"
class="border border-gray-300 dark:border-gray-600 rounded px-1 py-0 w-10 text-xs bg-white dark:bg-gray-900 text-gray-700 dark:text-gray-200 hover:border-cta-blue dark:hover:border-highlight-blue focus:border-cta-blue dark:focus:border-highlight-blue transition-colors duration-200"
style="height: 1.5rem;"
/>
</label>
</div>
</div>
<div style="height: 1.25rem;"></div>
<div class="mb-8"></div>
{#if chartData.length > 0}
<div class="grid grid-cols-4 gap-2 sm:gap-4">
{#each metrics as metric}
@@ -1065,11 +1053,12 @@
No trendline stats to display (trendStats is null or not enough data).
</div>
{/if}
<div class="mt-8 mb-6"></div>
{#key chartKey}
{#if containerReady}
<div
bind:this={chartContainer}
class="min-h-[220px] max-h-[280px] w-full box-border relative rounded-md bg-white dark:bg-gray-800 m-1 transition-colors duration-200"
class="min-h-[220px] max-h-[280px] w-full box-border relative rounded-md bg-white dark:bg-gray-900 m-1 transition-colors duration-200"
style="contain: layout style;"
></div>
{/if}
@@ -1126,8 +1115,6 @@
display: block !important;
contain: layout style !important;
background: white;
border-radius: 8px;
padding: 8px;
transition: background-color 0.2s ease;
}
:global(.dark .campaign-trend-chart) {
@@ -46,7 +46,7 @@
class:items-center={inline}
>
<div class="flex items-center">
<p class="font-semibold text-slate-600 dark:text-gray-300 py-2 transition-colors duration-200">
<p class="font-semibold text-slate-600 dark:text-gray-400 py-2 transition-colors duration-200">
<slot />
</p>
{#if toolTipText.length > 0}
@@ -56,9 +56,9 @@
{/if}
{#if optional === true}
<div
class="bg-gray-100 dark:bg-gray-700 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
class="bg-gray-100 dark:bg-gray-800/60 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
>
<p class="text-slate-600 dark:text-gray-300 text-xs transition-colors duration-200">
<p class="text-slate-600 dark:text-gray-400 text-xs transition-colors duration-200">
optional
</p>
</div>
@@ -75,12 +75,12 @@
on:change
/>
<div
class="w-5 h-5 border-2 border-slate-300 dark:border-gray-600 rounded
peer-checked:border-cta-blue dark:peer-checked:border-blue-500 peer-checked:bg-cta-blue dark:peer-checked:bg-blue-500
class="w-5 h-5 border-2 border-slate-300 dark:border-gray-700/60 rounded
peer-checked:border-cta-blue dark:peer-checked:border-highlight-blue/80 peer-checked:bg-cta-blue dark:peer-checked:bg-highlight-blue/80
transition-all duration-200 ease-in-out
flex items-center justify-center
bg-slate-50 dark:bg-gray-700
focus-within:ring-2 focus-within:ring-cta-blue dark:focus-within:ring-blue-500 focus-within:ring-offset-2 dark:focus-within:ring-offset-gray-800"
bg-slate-50 dark:bg-gray-900/60
focus-within:ring-2 focus-within:ring-cta-blue dark:focus-within:ring-highlight-blue/80 focus-within:ring-offset-2 dark:focus-within:ring-offset-gray-900"
>
{#if value}
<svg class="w-3 h-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
File diff suppressed because it is too large Load Diff
@@ -9,7 +9,7 @@
class:w-32={size === 'small'}
class:w-36={size === 'medium'}
class:w-60={size === 'large'}
class="bg-cta-blue dark:bg-blue-600 px-2 py-2 self-end hover:bg-blue-500 dark:hover:bg-blue-700 text-white text-sm font-bold flex justify-center items-center uppercase rounded-md transition-colors duration-200 disabled:opacity-50 dark:disabled:opacity-60"
class="bg-cta-blue dark:bg-highlight-blue/80 px-2 py-2 self-end hover:bg-blue-500 dark:hover:bg-highlight-blue text-white text-sm font-bold flex justify-center items-center uppercase rounded-md transition-colors duration-200 disabled:opacity-50 dark:disabled:opacity-60"
on:click
>
{#if isSubmitting}
+2 -2
View File
@@ -7,7 +7,7 @@
<div class="flex col-span-12 justify-center p-4 select-text">
<div class="w-80 flex col-span-12 justify-center">
<div
class="flex items-center w-full bg-pleasant-gray dark:bg-gray-700 rounded-md border border-gray-200 dark:border-gray-600 text-center p-2 font-titilium transition-colors duration-200"
class="flex items-center w-full bg-pleasant-gray dark:bg-gray-800/40 rounded-md border border-gray-200 dark:border-gray-700/60 text-center p-2 font-titilium transition-colors duration-200"
>
<svg class="w-12 ml-4" transition:slide viewBox="0 0 32.94 32.94">
<circle
@@ -33,7 +33,7 @@
/>
</g>
</svg>
<p class="pl-4 text-gray-700 dark:text-gray-200 transition-colors duration-200">
<p class="pl-4 text-gray-700 dark:text-gray-400 transition-colors duration-200">
{message}
</p>
</div>
+2 -2
View File
@@ -12,7 +12,7 @@
<div class="flex flex-col w-full p-4 h-24">
<label
for={fieldName}
class="text-md font-semibold font-titilium text-pc-darkblue dark:text-gray-200"
class="text-md font-semibold font-titilium text-pc-darkblue dark:text-gray-300"
>{fieldName}</label
>
<input
@@ -34,6 +34,6 @@
{type}
id={fieldName}
name={fieldName}
class="w-full p-2 rounded bg-pc-lightblue dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:placeholder-gray-400 focus:outline-none focus:ring-0 focus:border-cta-blue dark:focus:border-blue-500 focus:border-2 transition-colors duration-200"
class="w-full p-2 rounded bg-pc-lightblue dark:bg-gray-900/60 dark:border-gray-700/60 dark:text-gray-300 dark:placeholder-gray-500 focus:outline-none focus:ring-0 focus:border-cta-blue dark:focus:border-highlight-blue/80 focus:border-2 transition-colors duration-200"
/>
</div>
+2 -4
View File
@@ -295,9 +295,7 @@
{#if visible}
<div bind:this={bindTo}>
<div
class="fixed top-0 left-0 w-full h-full bg-cta-blue dark:bg-gray-900 opacity-20 blur-xl transition-colors duration-200"
/>
<div class="fixed top-0 left-0 w-full h-full opacity-[0.5]" />
<div
class="fixed top-0 left-0 w-full h-full flex justify-center items-center backdrop-blur-sm z-20"
role="dialog"
@@ -311,7 +309,7 @@
>
<div
class:opacity-20={isSubmitting}
class="bg-cta-blue dark:bg-blue-700 text-white rounded-t-md py-4 px-8 flex justify-between flex-shrink-0 transition-colors duration-200"
class="bg-cta-blue dark:bg-blue-800 text-white rounded-t-md py-4 px-8 flex justify-between flex-shrink-0 transition-colors duration-200"
>
<div class="flex-1">
<h1 id="modal-title" class="uppercase mr-8 font-semibold text-2xl">{headerText}</h1>
@@ -43,16 +43,16 @@
<div class="flex items-center mb-8 mt-4">
<button
class="bg-highlight-blue dark:bg-blue-600 w-8 text-white hover:bg-active-blue dark:hover:bg-blue-700 m-1 rounded-md py-1 px-1 transition-colors duration-200"
class="bg-highlight-blue dark:bg-highlight-blue/80 w-8 text-white hover:bg-active-blue dark:hover:bg-highlight-blue m-1 rounded-md py-1 px-1 transition-colors duration-200"
on:click|preventDefault={previousPage}>&lt;&lt;</button
>
<div
class="w-8 text-center bg-grayblue-light dark:bg-gray-700 text-gray-700 dark:text-gray-200 rounded-md py-1 px-1 transition-colors duration-200"
class="w-8 text-center bg-grayblue-light dark:bg-gray-800/60 text-gray-700 dark:text-gray-300 rounded-md py-1 px-1 transition-colors duration-200"
>
{currentPage}
</div>
<button
class="bg-highlight-blue dark:bg-blue-600 w-8 text-white hover:bg-active-blue dark:hover:bg-blue-700 m-1 rounded-md py-1 px-1 transition-colors duration-200"
class="bg-highlight-blue dark:bg-highlight-blue/80 w-8 text-white hover:bg-active-blue dark:hover:bg-highlight-blue m-1 rounded-md py-1 px-1 transition-colors duration-200"
on:click|preventDefault={nextPage}>&gt;&gt;</button
>
</div>
@@ -59,7 +59,7 @@
<label class="flex flex-col py-2 w-60">
<div class="flex items-center">
<p class="font-semibold text-slate-600 dark:text-gray-300 py-2 transition-colors duration-200">
<p class="font-semibold text-slate-600 dark:text-gray-400 py-2 transition-colors duration-200">
<slot />
</p>
{#if toolTipText.length > 0}
@@ -69,9 +69,9 @@
{/if}
{#if optional === true}
<div
class="bg-gray-100 dark:bg-gray-700 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
class="bg-gray-100 dark:bg-gray-800/60 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
>
<p class="text-slate-600 dark:text-gray-300 text-xs transition-colors duration-200">
<p class="text-slate-600 dark:text-gray-400 text-xs transition-colors duration-200">
optional
</p>
</div>
@@ -90,7 +90,7 @@
minlength={minLength}
maxlength={maxLength}
{required}
class="text-ellipsis w-60 rounded-md py-2 pl-4 pr-12 text-gray-600 dark:text-gray-200 border border-transparent dark:border-gray-600 focus:outline-none focus:border-solid focus:border-slate-400 dark:focus:border-blue-500 focus:bg-gray-100 dark:focus:bg-gray-600 bg-grayblue-light dark:bg-gray-700 font-normal transition-colors duration-200"
class="text-ellipsis w-60 rounded-md py-2 pl-4 pr-12 text-gray-600 dark:text-gray-300 border border-transparent dark:border-gray-700/60 focus:outline-none focus:border-solid focus:border-slate-400 dark:focus:border-highlight-blue/80 focus:bg-gray-100 dark:focus:bg-gray-700/60 bg-grayblue-light dark:bg-gray-900/60 font-normal transition-colors duration-200"
/>
{:else}
<input
@@ -104,7 +104,7 @@
{placeholder}
autocomplete="off"
{required}
class="text-ellipsis w-60 rounded-md py-2 pl-4 pr-12 text-gray-600 dark:text-gray-200 border border-transparent dark:border-gray-600 focus:outline-none focus:border-solid focus:border-slate-400 dark:focus:border-blue-500 focus:bg-gray-100 dark:focus:bg-gray-600 bg-grayblue-light dark:bg-gray-700 font-normal transition-colors duration-200"
class="text-ellipsis w-60 rounded-md py-2 pl-4 pr-12 text-gray-600 dark:text-gray-300 border border-transparent dark:border-gray-700/60 focus:outline-none focus:border-solid focus:border-slate-400 dark:focus:border-highlight-blue/80 focus:bg-gray-100 dark:focus:bg-gray-700/60 bg-grayblue-light dark:bg-gray-900/60 font-normal transition-colors duration-200"
/>
{/if}
<button
+1 -1
View File
@@ -39,7 +39,7 @@
setSearch();
}
}}
class="bg-grayblue-light dark:bg-gray-700 w-56 border text-gray-600 dark:text-gray-200 border-gray-300 dark:border-gray-600 pl-8 py-2 relative rounded-lg focus:outline-none focus:ring-0 focus:border-cta-blue dark:focus:border-blue-500 focus:border transition-colors duration-200 dark:placeholder-gray-400"
class="bg-grayblue-light dark:bg-gray-900/60 w-56 border text-gray-600 dark:text-gray-300 border-gray-300 dark:border-gray-700/60 pl-8 py-2 relative rounded-lg focus:outline-none focus:ring-0 focus:border-cta-blue dark:focus:border-highlight-blue/80 focus:border transition-colors duration-200 dark:placeholder-gray-500"
placeholder="Search"
/>
</div>
+2 -2
View File
@@ -22,11 +22,11 @@
</script>
<div>
<label for="pet-select" class="text-gray-600 dark:text-gray-300 transition-colors duration-200"
<label for="pet-select" class="text-gray-600 dark:text-gray-400 transition-colors duration-200"
>Show:</label
>
<select
class="bg-grayblue-light dark:bg-gray-700 px-2 py-1 rounded-md text-gray-600 dark:text-gray-200 border border-transparent dark:border-gray-600 focus:outline-none focus:border-solid focus:border focus:border-slate-400 dark:focus:border-blue-500 focus:bg-gray-100 dark:focus:bg-gray-600 transition-colors duration-200"
class="bg-grayblue-light dark:bg-gray-900/60 px-2 py-1 rounded-md text-gray-600 dark:text-gray-300 border border-transparent dark:border-gray-700/60 focus:outline-none focus:border-solid focus:border focus:border-slate-400 dark:focus:border-highlight-blue/80 focus:bg-gray-100 dark:focus:bg-gray-700/60 transition-colors duration-200"
name="entries"
id="entries"
bind:value
@@ -15,7 +15,7 @@
<div class="flex flex-col gap-2 py-2">
{#if label}
<div class="flex flex-row items-center">
<div class="font-semibold text-slate-600 dark:text-gray-300 transition-colors duration-200">
<div class="font-semibold text-slate-600 dark:text-gray-400 transition-colors duration-200">
{label}
</div>
{#if toolTipText.length > 0}
@@ -25,9 +25,9 @@
{/if}
{#if optional}
<div
class="bg-gray-100 dark:bg-gray-700 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
class="bg-gray-100 dark:bg-gray-800/60 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
>
<p class="text-slate-600 dark:text-gray-300 text-xs">optional</p>
<p class="text-slate-600 dark:text-gray-400 text-xs">optional</p>
</div>
{/if}
</div>
@@ -46,11 +46,11 @@
p-3 rounded-lg border-2 transition-all duration-200
flex flex-col items-center justify-center text-center
w-40
hover:border-blue-300 dark:hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/30
hover:border-blue-300 dark:hover:border-highlight-blue/80 hover:bg-blue-50 dark:hover:bg-highlight-blue/20
${
value === option.value
? 'border-green-500 dark:border-green-400 bg-green-50 dark:bg-green-900/30 text-green-700 dark:text-green-300'
: 'border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-200'
? 'border-green-500 dark:border-green-400 bg-green-50 dark:bg-green-800/40 text-green-700 dark:text-green-300'
: 'border-gray-200 dark:border-gray-700/60 bg-white dark:bg-gray-900/60 text-gray-700 dark:text-gray-300'
}
`}
on:click={() => {
+6 -6
View File
@@ -43,17 +43,17 @@
</script>
<div
class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md dark:shadow-gray-900/50 border-l-[12px] {borderColor} hover:shadow-lg dark:hover:shadow-gray-900/70 transition-all duration-200"
class="bg-white dark:bg-gray-900/80 p-6 rounded-lg shadow-md dark:shadow-none border-l-[12px] {borderColor} hover:shadow-lg dark:hover:shadow-none transition-all duration-200 dark:ring-1 dark:ring-gray-600/30"
>
<div
class="text-grayblue-dark dark:text-gray-400 text-sm font-semibold transition-colors duration-200"
class="text-grayblue-dark dark:text-gray-500 text-sm font-semibold transition-colors duration-200"
>
{title}
</div>
<div class="flex items-center justify-between">
<div class="flex items-center">
<span
class="text-3xl font-bold text-pc-darkblue dark:text-gray-100 transition-colors duration-200 {flash
class="text-3xl font-bold text-pc-darkblue dark:text-gray-300 transition-colors duration-200 {flash
? 'flash'
: ''}"
>
@@ -82,13 +82,13 @@
{#if validPercentages.length > 0}
<button
class="mt-2 text-sm text-gray-600 dark:text-gray-400 flex items-center transition-colors duration-200"
class="mt-2 text-sm text-gray-600 dark:text-gray-500 flex items-center transition-colors duration-200"
on:click={cyclePercentage}
class:cursor-pointer={validPercentages.length > 1}
>
<div class="flex items-center">
<span
class="text-pc-darkblue dark:text-gray-200 font-semibold transition-colors duration-200"
class="text-pc-darkblue dark:text-gray-400 font-semibold transition-colors duration-200"
>
{validPercentages[currentPercentageIndex].value}%
</span>
@@ -99,7 +99,7 @@
{#if validPercentages.length > 1}
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4 ml-1 text-gray-400 dark:text-gray-500 transition-colors duration-200"
class="h-4 w-4 ml-1 text-gray-400 dark:text-gray-600 transition-colors duration-200"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
+4 -4
View File
@@ -59,7 +59,7 @@
<label class="flex flex-col py-2">
<div class="flex items-center">
<p class="font-semibold text-slate-600 dark:text-gray-300 py-2 transition-colors duration-200">
<p class="font-semibold text-slate-600 dark:text-gray-400 py-2 transition-colors duration-200">
<slot />
</p>
{#if toolTipText.length > 0}
@@ -69,9 +69,9 @@
{/if}
{#if optional === true}
<div
class="bg-gray-100 dark:bg-gray-700 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
class="bg-gray-100 dark:bg-gray-800/60 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
>
<p class="text-slate-600 dark:text-gray-300 text-xs transition-colors duration-200">
<p class="text-slate-600 dark:text-gray-400 text-xs transition-colors duration-200">
optional
</p>
</div>
@@ -97,7 +97,7 @@
{required}
{placeholder}
{pattern}
class="text-ellipsis row-start-1 row-span-3 justify-self-center rounded-md py-2 pl-2 text-gray-600 dark:text-gray-200 border border-transparent dark:border-gray-600 focus:outline-none focus:border-solid focus:border-slate-400 dark:focus:border-blue-500 focus:bg-gray-100 dark:focus:bg-gray-600 bg-grayblue-light dark:bg-gray-700 font-normal transition-colors duration-200"
class="text-ellipsis row-start-1 row-span-3 justify-self-center rounded-md py-2 pl-2 text-gray-600 dark:text-gray-300 border border-transparent dark:border-gray-700/60 focus:outline-none focus:border-solid focus:border-slate-400 dark:focus:border-highlight-blue/80 focus:bg-gray-100 dark:focus:bg-gray-700/60 bg-grayblue-light dark:bg-gray-900/60 font-normal transition-colors duration-200"
class:w-24={width === 'small'}
class:w-60={width === 'medium'}
class:w-95={width === 'large'}
@@ -118,7 +118,7 @@
<label class="flex flex-col py-2 relative">
<div class="flex items-center">
<p
class="font-semibold text-slate-600 dark:text-gray-300 py-2 transition-colors duration-200"
class="font-semibold text-slate-600 dark:text-gray-400 py-2 transition-colors duration-200"
>
<slot />
</p>
@@ -129,9 +129,9 @@
{/if}
{#if optional === true}
<div
class="bg-gray-100 dark:bg-gray-700 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
class="bg-gray-100 dark:bg-gray-800/60 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
>
<p class="text-slate-600 dark:text-gray-300 text-xs">optional</p>
<p class="text-slate-600 dark:text-gray-400 text-xs">optional</p>
</div>
{/if}
</div>
@@ -155,7 +155,7 @@
{id}
required={required && !value.length}
autocomplete="off"
class="w-full relative rounded-md py-2 pl-4 focus:pl-10 text-gray-600 dark:text-gray-300 border border-transparent focus:outline-none focus:border-solid focus:border focus:border-slate-400 dark:focus:border-gray-500 focus:bg-gray-100 dark:focus:bg-gray-600 bg-grayblue-light dark:bg-gray-700 font-normal cursor-pointer focus:cursor-text transition-colors duration-200"
class="w-full relative rounded-md py-2 pl-4 focus:pl-10 text-gray-600 dark:text-gray-300 border border-transparent focus:outline-none focus:border-solid focus:border focus:border-slate-400 dark:focus:border-highlight-blue/80 focus:bg-gray-100 dark:focus:bg-gray-700/60 bg-grayblue-light dark:bg-gray-900/60 font-normal cursor-pointer focus:cursor-text transition-colors duration-200"
/>
{#if showSelection}
<img
@@ -169,13 +169,13 @@
{#if showSelection}
<div class="w-60 absolute top-10 z-50">
<ul
class="bg-gray-100 dark:bg-gray-700 list-none mt-4 rounded-md min-w-fit shadow-md border border-gray-200 dark:border-gray-600 max-h-40 overflow-y-scroll transition-colors duration-200"
class="bg-gray-100 dark:bg-gray-900 list-none mt-4 rounded-md min-w-fit shadow-md border border-gray-200 dark:border-gray-700/60 max-h-40 overflow-y-scroll transition-colors duration-200"
>
{#if options.length}
{#each filteredOptions as option}
<li>
<button
class="w-full text-left bg-slate-100 dark:bg-gray-700 rounded-md text-gray-600 dark:text-gray-200 hover:bg-grayblue-dark dark:hover:bg-gray-500 hover:text-white py-2 px-2 cursor-pointer transition-colors duration-200"
class="w-full text-left bg-slate-100 dark:bg-gray-900 rounded-md text-gray-600 dark:text-gray-300 hover:bg-grayblue-dark dark:hover:bg-highlight-blue/40 hover:text-white py-2 px-2 cursor-pointer transition-colors duration-200"
on:click={() => {
onClickSelectedOption(option);
}}
@@ -186,7 +186,7 @@
{/each}
{:else}
<li
class="w-full bg-slate-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 py-2 px-2 transition-colors duration-200"
class="w-full bg-slate-100 dark:bg-gray-900 text-gray-600 dark:text-gray-400 py-2 px-2 transition-colors duration-200"
>
List is empty
</li>
@@ -200,7 +200,7 @@
on:click|preventDefault={removeSelection}
on:keypress|preventDefault={removeSelection}
data-value={option}
class="flex flex-row items-center bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 px-2 py-2 mt-2 mr-2 rounded-md text-gray-900 dark:text-gray-100 transition-colors duration-200"
class="flex flex-row items-center bg-gray-100 dark:bg-gray-800/60 hover:bg-gray-200 dark:hover:bg-gray-700/80 px-2 py-2 mt-2 mr-2 rounded-md text-gray-900 dark:text-gray-300 transition-colors duration-200"
>
{option}
<img class="w-4 ml-2 pointer-events-none" src="/delete2.svg" alt="delete" />
@@ -237,7 +237,7 @@
<div class="flex items-center">
<p
id={labelId}
class="font-semibold text-slate-600 dark:text-gray-300 py-1 transition-colors duration-200"
class="font-semibold text-slate-600 dark:text-gray-400 py-1 transition-colors duration-200"
>
<slot />
</p>
@@ -248,9 +248,9 @@
{/if}
{#if optional === true}
<div
class="bg-gray-100 dark:bg-gray-700 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
class="bg-gray-100 dark:bg-gray-800/60 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
>
<p class="text-slate-600 dark:text-gray-300 text-xs">optional</p>
<p class="text-slate-600 dark:text-gray-400 text-xs">optional</p>
</div>
{/if}
</div>
@@ -278,7 +278,7 @@
on:keydown={handleKeyDown}
on:click={handleFocus}
autocomplete="off"
class="w-full relative rounded-md py-2 pr-10 text-gray-600 dark:text-gray-300 border border-transparent focus:outline-none focus:border-solid focus:border focus:border-slate-400 dark:focus:border-gray-500 focus:bg-gray-100 dark:focus:bg-gray-600 bg-grayblue-light dark:bg-gray-700 font-normal cursor-pointer focus:cursor-text transition-colors duration-200"
class="w-full relative rounded-md py-2 pr-10 text-gray-600 dark:text-gray-300 border border-transparent focus:outline-none focus:border-solid focus:border focus:border-slate-400 dark:focus:border-highlight-blue/80 focus:bg-gray-100 dark:focus:bg-gray-700/60 bg-grayblue-light dark:bg-gray-900/60 font-normal cursor-pointer focus:cursor-text transition-colors duration-200"
class:pl-10={showDropdown}
class:pl-4={!showDropdown}
class:text-gray-400={!hasValue && !showDropdown}
@@ -331,7 +331,7 @@
id={listboxId}
role="listbox"
aria-labelledby={labelId}
class="bg-gray-100 dark:bg-gray-700 list-none mt-4 z-[999] rounded-md min-w-fit shadow-md border border-gray-200 dark:border-gray-600 max-h-40 overflow-y-scroll transition-colors duration-200"
class="bg-gray-100 dark:bg-gray-900 list-none mt-4 z-[999] rounded-md min-w-fit shadow-md border border-gray-200 dark:border-gray-700/60 max-h-40 overflow-y-scroll transition-colors duration-200"
>
{#if allOptions.length}
{#each allOptions as option, index}
@@ -340,7 +340,7 @@
id="{listboxId}-option-{index}"
role="option"
aria-selected={value === option}
class="w-full text-left bg-slate-100 dark:bg-gray-700 rounded-md text-gray-600 dark:text-gray-200 hover:bg-grayblue-dark dark:hover:bg-gray-500 hover:text-white py-2 px-2 cursor-pointer focus:bg-grayblue-dark dark:focus:bg-gray-500 focus:text-white focus:outline-none transition-colors duration-200"
class="w-full text-left bg-slate-100 dark:bg-gray-900 rounded-md text-gray-600 dark:text-gray-300 hover:bg-grayblue-dark dark:hover:bg-highlight-blue/40 hover:text-white py-2 px-2 cursor-pointer focus:bg-grayblue-dark dark:focus:bg-highlight-blue/40 focus:text-white focus:outline-none transition-colors duration-200"
on:click={(e) => {
e.preventDefault();
e.stopPropagation();
@@ -356,7 +356,7 @@
{:else}
<li
role="none"
class="w-full bg-slate-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 py-2 px-2 transition-colors duration-200"
class="w-full bg-slate-100 dark:bg-gray-900 text-gray-600 dark:text-gray-400 py-2 px-2 transition-colors duration-200"
>
No options available
</li>
@@ -52,7 +52,7 @@
<label class="flex flex-col py-2 w-60" class:w-full={fullWidth}>
<div class="flex items-center">
<p class="font-bold text-slate-600 dark:text-gray-300 py-2 transition-colors duration-200">
<p class="font-bold text-slate-600 dark:text-gray-400 py-2 transition-colors duration-200">
<slot />
</p>
{#if toolTipText.length > 0}
@@ -62,9 +62,9 @@
{/if}
{#if optional === true}
<div
class="bg-gray-100 dark:bg-gray-700 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
class="bg-gray-100 dark:bg-gray-800/60 ml-2 px-2 rounded-md transition-colors duration-200 h-6 flex items-center"
>
<p class="text-slate-600 dark:text-gray-300 text-xs transition-colors duration-200">
<p class="text-slate-600 dark:text-gray-400 text-xs transition-colors duration-200">
optional
</p>
</div>
@@ -80,7 +80,7 @@
maxlength={maxLength}
{readonly}
{placeholder}
class=" focus:outline-none pl-2 border border-transparent dark:border-gray-600 rounded-md focus:border-solid text-gray-600 dark:text-gray-200 focus:bg-gray-100 dark:focus:bg-gray-600 font-light focus:border-slate-400 dark:focus:border-blue-500 bg-grayblue-light dark:bg-gray-700 transition-colors duration-200"
class=" focus:outline-none pl-2 border border-transparent dark:border-gray-700/60 rounded-md focus:border-solid text-gray-600 dark:text-gray-300 focus:bg-gray-100 dark:focus:bg-gray-700/60 font-light focus:border-slate-400 dark:focus:border-highlight-blue/80 bg-grayblue-light dark:bg-gray-900/60 transition-colors duration-200"
class:h-16={height === 'small'}
class:h-28={height === 'medium'}
class:h-48={height === 'large'}
+4 -4
View File
@@ -30,7 +30,7 @@
}
}}
transition:slide|global
class="flex items-center bg-pleasant-gray dark:bg-gray-700 text-gray-500 dark:text-gray-200 shadow-lg dark:shadow-gray-900/50 capitalize text-xl p-4 first:mt-0 mt-4 min-w-max rounded-md justify-self-center w-full transition-colors duration-200"
class="flex items-center bg-pleasant-gray dark:bg-gray-800/80 text-gray-500 dark:text-gray-300 shadow-lg dark:shadow-gray-900/50 capitalize text-xl p-4 first:mt-0 mt-4 min-w-max rounded-md justify-self-center w-full transition-colors duration-200"
>
<!-- <img class="w-9 mr-6" draggable="false" src={src1} alt="checkmark success" /> -->
<svg class="w-11 mr-6" viewBox="0 0 32.25 32.4">
@@ -60,7 +60,7 @@
}
}}
transition:slide|global
class="flex items-center bg-pleasant-gray dark:bg-gray-700 text-gray-500 dark:text-gray-200 shadow-lg dark:shadow-gray-900/50 capitalize text-xl p-4 first:mt-0 mt-4 min-w-max rounded-md justify-self-center w-full transition-colors duration-200"
class="flex items-center bg-pleasant-gray dark:bg-gray-800/80 text-gray-500 dark:text-gray-300 shadow-lg dark:shadow-gray-900/50 capitalize text-xl p-4 first:mt-0 mt-4 min-w-max rounded-md justify-self-center w-full transition-colors duration-200"
>
<!-- <img class="w-9 mr-6" draggable="false" src="/t-warning.svg" alt="checkmark warning" /> -->
<svg class="w-11 mr-6" viewBox="0 0 36.26 32.41">
@@ -101,7 +101,7 @@
}
}}
transition:slide|global
class="flex items-center bg-pleasant-gray dark:bg-gray-700 text-gray-500 dark:text-gray-200 shadow-lg dark:shadow-gray-900/50 capitalize text-xl p-4 first:mt-0 mt-4 min-w-max rounded-md justify-self-center w-full transition-colors duration-200"
class="flex items-center bg-pleasant-gray dark:bg-gray-800/80 text-gray-500 dark:text-gray-300 shadow-lg dark:shadow-gray-900/50 capitalize text-xl p-4 first:mt-0 mt-4 min-w-max rounded-md justify-self-center w-full transition-colors duration-200"
>
<!-- <img class="w-9 mr-6" draggable="false" src="/t-error.svg" alt="checkmark error" /> -->
<svg class="w-11 mr-6" viewBox="0 0 32.98 32.98">
@@ -143,7 +143,7 @@
removeToast(toast.id);
}
}}
class="flex items-center bg-pleasant-gray dark:bg-gray-700 text-gray-500 dark:text-gray-200 shadow-lg dark:shadow-gray-900/50 capitalize text-xl p-4 first:mt-0 mt-4 min-w-max rounded-md justify-self-center w-full transition-colors duration-200"
class="flex items-center bg-pleasant-gray dark:bg-gray-800/80 text-gray-500 dark:text-gray-300 shadow-lg dark:shadow-gray-900/50 capitalize text-xl p-4 first:mt-0 mt-4 min-w-max rounded-md justify-self-center w-full transition-colors duration-200"
>
<!-- <img class="w-9 mr-6" draggable="false" src="/t-info.svg" alt="checkmark info" /> -->
<svg class="w-11 mr-6" viewBox="0 0 32.79 32.79">
+2 -2
View File
@@ -10,7 +10,7 @@
</script>
<div
class="rounded-full bg-gray-600 dark:bg-gray-500 text-white w-4 h-4 z-30 text-center ml-2 relative cursor-pointer hover:bg-gray-500 dark:hover:bg-gray-400 transition-colors duration-200"
class="rounded-full bg-gray-600 dark:bg-gray-700/80 text-white w-4 h-4 z-30 text-center ml-2 relative cursor-pointer hover:bg-gray-500 dark:hover:bg-gray-600/80 transition-colors duration-200"
role="tooltip"
aria-describedby={tooltipId}
on:mouseenter={(e) => {
@@ -27,7 +27,7 @@
{#if showToolTip}
<div
id={tooltipId}
class="bg-gray-600 dark:bg-gray-700 text-white w-max mt-2 px-2 py-2 rounded-md shadow-xl dark:shadow-gray-900/70 z-40 transition-colors duration-200"
class="bg-gray-600 dark:bg-gray-800/90 text-white w-max mt-2 px-2 py-2 rounded-md shadow-xl dark:shadow-gray-900/70 z-40 transition-colors duration-200"
style={tooltipStyle}
>
<p><slot /></p>
@@ -16,14 +16,14 @@
}
</script>
<div class="inline-flex bg-gray-100 dark:bg-gray-700 rounded p-0.5">
<div class="inline-flex bg-gray-100 dark:bg-gray-800/60 rounded p-0.5">
{#each options as option}
<button
type="button"
class="w-6 h-6 text-xs rounded transition-colors duration-150 flex items-center justify-center {value ===
option.value
? 'bg-white dark:bg-gray-600 text-gray-900 dark:text-white shadow-sm'
: 'text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white'} {disabled
? 'bg-white dark:bg-gray-700/80 text-gray-900 dark:text-white shadow-sm'
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white'} {disabled
? 'opacity-50 cursor-not-allowed'
: 'cursor-pointer'}"
on:click={() => handleChange(option.value)}
@@ -240,7 +240,7 @@
<div class="flex items-center">
<p
id={labelId}
class="font-semibold text-slate-600 dark:text-gray-300 py-1 transition-colors duration-200"
class="font-semibold text-slate-600 dark:text-gray-400 py-1 transition-colors duration-200"
>
<slot />
</p>
@@ -251,9 +251,9 @@
{/if}
{#if optional === true}
<div
class="bg-gray-100 dark:bg-gray-700 ml-2 px-2 rounded-md transition-colors duration-200"
class="bg-gray-100 dark:bg-gray-800/60 ml-2 px-2 rounded-md transition-colors duration-200"
>
<p class="text-slate-600 dark:text-gray-300 text-xs">optional</p>
<p class="text-slate-600 dark:text-gray-400 text-xs">optional</p>
</div>
{/if}
</div>
@@ -265,8 +265,8 @@
type="button"
class="w-8 h-8 text-base transition-all duration-200 rounded-lg border flex items-center justify-center {type ===
'page'
? 'border-green-500 dark:border-green-400 bg-green-50 dark:bg-green-900/30 text-green-700 dark:text-green-300'
: 'border-gray-200 dark:border-gray-600 bg-grayblue-light dark:bg-gray-700 text-gray-700 dark:text-gray-200 hover:border-blue-300 dark:hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/30'}"
? 'border-green-500 dark:border-green-400 bg-green-50 dark:bg-green-800/40 text-green-700 dark:text-green-300'
: 'border-gray-200 dark:border-gray-700/60 bg-grayblue-light dark:bg-gray-800/60 text-gray-700 dark:text-gray-300 hover:border-blue-300 dark:hover:border-highlight-blue/80 hover:bg-blue-50 dark:hover:bg-highlight-blue/20'}"
on:click={() => handleTypeChange('page')}
title="Page"
>
@@ -276,8 +276,8 @@
type="button"
class="w-8 h-8 text-base transition-all duration-200 rounded-lg border flex items-center justify-center ml-0.5 {type ===
'proxy'
? 'border-green-500 dark:border-green-400 bg-green-50 dark:bg-green-900/30 text-green-700 dark:text-green-300'
: 'border-gray-200 dark:border-gray-600 bg-grayblue-light dark:bg-gray-700 text-gray-700 dark:text-gray-200 hover:border-blue-300 dark:hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/30'}"
? 'border-green-500 dark:border-green-400 bg-green-50 dark:bg-green-800/40 text-green-700 dark:text-green-300'
: 'border-gray-200 dark:border-gray-700/60 bg-grayblue-light dark:bg-gray-800/60 text-gray-700 dark:text-gray-300 hover:border-blue-300 dark:hover:border-highlight-blue/80 hover:bg-blue-50 dark:hover:bg-highlight-blue/20'}"
on:click={() => handleTypeChange('proxy')}
title="Proxy"
>
@@ -308,7 +308,7 @@
on:keydown={handleKeyDown}
on:click={handleFocus}
autocomplete="off"
class="w-full relative rounded-md py-2 pr-10 text-gray-600 dark:text-gray-300 border border-transparent focus:outline-none focus:border-solid focus:border focus:border-slate-400 dark:focus:border-gray-500 focus:bg-gray-100 dark:focus:bg-gray-600 bg-grayblue-light dark:bg-gray-700 font-normal cursor-pointer focus:cursor-text transition-colors duration-200"
class="w-full relative rounded-md py-2 pr-10 text-gray-600 dark:text-gray-300 border border-transparent focus:outline-none focus:border-solid focus:border focus:border-slate-400 dark:focus:border-highlight-blue/80 focus:bg-gray-100 dark:focus:bg-gray-700/60 bg-grayblue-light dark:bg-gray-900/60 font-normal cursor-pointer focus:cursor-text transition-colors duration-200"
class:pl-10={isFocused && showDropdown}
class:pl-20={!isFocused}
class:pl-4={isFocused && !showDropdown}
@@ -363,7 +363,7 @@
id={listboxId}
role="listbox"
aria-labelledby={labelId}
class="bg-gray-100 dark:bg-gray-700 list-none mt-4 z-[999] rounded-md min-w-fit shadow-md border border-gray-200 dark:border-gray-600 max-h-40 overflow-y-scroll transition-colors duration-200"
class="bg-gray-100 dark:bg-gray-900 list-none mt-4 z-[999] rounded-md min-w-fit shadow-md border border-gray-200 dark:border-gray-700/60 max-h-40 overflow-y-scroll transition-colors duration-200"
>
{#if allOptions.length}
{#each allOptions as option, index}
@@ -372,7 +372,7 @@
id="{listboxId}-option-{index}"
role="option"
aria-selected={value === option}
class="w-full text-left bg-slate-100 dark:bg-gray-700 rounded-md text-gray-600 dark:text-gray-200 hover:bg-grayblue-dark dark:hover:bg-gray-500 hover:text-white py-2 px-2 cursor-pointer focus:bg-grayblue-dark dark:focus:bg-gray-500 focus:text-white focus:outline-none transition-colors duration-200"
class="w-full text-left bg-slate-100 dark:bg-gray-900 rounded-md text-gray-600 dark:text-gray-300 hover:bg-grayblue-dark dark:hover:bg-highlight-blue/40 hover:text-white py-2 px-2 cursor-pointer focus:bg-grayblue-dark dark:focus:bg-highlight-blue/40 focus:text-white focus:outline-none transition-colors duration-200"
on:click={(e) => {
e.preventDefault();
e.stopPropagation();
@@ -388,7 +388,7 @@
{:else}
<li
role="none"
class="w-full bg-slate-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 py-2 px-2 transition-colors duration-200"
class="w-full bg-slate-100 dark:bg-gray-900 text-gray-600 dark:text-gray-400 py-2 px-2 transition-colors duration-200"
>
No {type === 'proxy' ? 'Proxies' : 'pages'} available
</li>
@@ -0,0 +1,53 @@
<script>
import { AppStateService } from '$lib/service/appState';
import { onMount } from 'svelte';
let context = {
current: '',
companyName: ''
};
const appState = AppStateService.instance;
onMount(() => {
const unsub = appState.subscribe((s) => {
context = {
current: s.context.current,
companyName: s.context.companyName
};
});
return () => {
unsub();
};
});
</script>
{#if context.current === AppStateService.CONTEXT.COMPANY && context.companyName}
<div
class="sticky top-0 w-full bg-blue-900 dark:bg-gradient-to-r dark:from-emerald-700 dark:to-teal-700 border-b border-blue-800/50 dark:border-emerald-600/50 z-30"
>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-center py-1">
<div class="flex items-center space-x-2 text-xs">
<svg
class="w-3 h-3 text-blue-100 dark:text-emerald-100"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"
></path>
</svg>
<span class="text-blue-100 dark:text-emerald-100 font-medium">Viewing as:</span>
<span class="text-white dark:text-white font-semibold">
{context.companyName}
</span>
</div>
</div>
</div>
</div>
{/if}
@@ -3,8 +3,57 @@
import { page } from '$app/stores';
import { scrollBarClassesVertical } from '$lib/utils/scrollbar';
import { shouldHideMenuItem } from '$lib/utils/common';
import { AppStateService } from '$lib/service/appState';
import { onMount } from 'svelte';
import { beforeNavigate } from '$app/navigation';
let isExpanded = false;
let menuElement;
let instantCollapse = false;
let context = {
current: '',
companyName: ''
};
const appState = AppStateService.instance;
onMount(() => {
const unsub = appState.subscribe((s) => {
context = {
current: s.context.current,
companyName: s.context.companyName
};
});
// handle click outside to collapse menu
const handleClickOutside = (event) => {
if (isExpanded && menuElement && !menuElement.contains(event.target)) {
isExpanded = false;
}
};
document.addEventListener('click', handleClickOutside);
return () => {
unsub();
document.removeEventListener('click', handleClickOutside);
};
});
// handle navigation to collapse menu
beforeNavigate(() => {
if (isExpanded) {
instantCollapse = true;
isExpanded = false;
// reset after a brief moment
setTimeout(() => {
instantCollapse = false;
}, 50);
}
});
$: hasCompanySelected =
context.current === AppStateService.CONTEXT.COMPANY && context.companyName;
const icons = {
dashboard: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
@@ -98,19 +147,23 @@
<div class="flex">
<nav
class="hidden lg:flex flex-col transition-all fixed top-16 z-10 bg-gradient-to-b from-pc-darkblue to-indigo-400 dark:from-gray-900 dark:to-gray-800 rounded-br-lg overflow-y-auto overflow-x-hidden min-h-0 max-h-[calc(100vh-4rem)] box-content border-r-[1px] border-pc-darkblue dark:border-gray-700"
bind:this={menuElement}
class="hidden lg:flex flex-col fixed top-16 z-10 bg-gradient-to-b from-pc-darkblue to-indigo-400 dark:from-gray-900 dark:to-gray-800 rounded-br-lg overflow-y-auto overflow-x-hidden min-h-0 max-h-[calc(100vh-4rem)] box-content border-r-[1px] border-pc-darkblue dark:border-highlight-blue/40"
class:transition-all={!instantCollapse}
class:w-40={isExpanded}
class:w-12={!isExpanded}
class:!top-[89px]={hasCompanySelected}
class:!max-h-[calc(100vh-6rem)]={hasCompanySelected}
>
<div
class="sticky top-0 bg-highlight-blue/20 dark:bg-gray-800/70 border-b w-full border-blue-700/30 dark:border-gray-600 transform-none transition-colors duration-200"
class="sticky top-0 bg-highlight-blue/20 dark:bg-gray-800/70 border-b w-full border-blue-700/30 dark:border-highlight-blue/40 transform-none transition-colors duration-200"
>
<button
class="w-full flex items-center justify-center rounded-md hover:bg-blue-600/30 dark:hover:bg-gray-700/70 transition-colors group px-3 py-2"
class="w-full flex items-center justify-center rounded-md hover:bg-blue-600/30 dark:hover:bg-highlight-blue/20 transition-colors group px-3 py-2"
on:click={() => (isExpanded = !isExpanded)}
>
<svg
class="text-blue-100 dark:text-gray-100 duration-200 w-6 transition-colors"
class="text-blue-100 dark:text-highlight-blue duration-200 w-6 transition-colors"
class:rotate-180={!isExpanded}
xmlns="http://www.w3.org/2000/svg"
fill="none"
@@ -136,7 +189,7 @@
<div class="py-1 mt-4 first:mt-0">
{#if isExpanded}
<div
class="px-3 py-2 text-xs font-semibold text-blue-100 dark:text-gray-200 uppercase tracking-wider transition-colors duration-200"
class="px-3 py-2 text-xs font-semibold text-blue-100 dark:text-highlight-blue uppercase tracking-wider transition-colors duration-200"
>
{link.label}
</div>
@@ -147,15 +200,15 @@
<a
class="flex items-center px-3 py-2 text-sm transition-all duration-150 relative group
{$page.url.pathname === item.route
? 'text-white font-medium bg-active-blue dark:bg-indigo-600 shadow-md'
: 'text-blue-100 dark:text-gray-200 hover:shadow-md hover:bg-highlight-blue dark:hover:bg-gray-700 hover:text-white dark:hover:text-gray-100'}"
? 'text-white font-medium bg-active-blue dark:bg-active-blue shadow-md'
: 'text-blue-100 dark:text-gray-200 hover:shadow-md hover:bg-highlight-blue/80 dark:hover:bg-highlight-blue/20 hover:text-white dark:hover:text-gray-100'}"
class:hidden={shouldHideMenuItem(item.route)}
draggable="false"
href={item.route}
title={item.label}
>
<!-- Icon -->
<div class="flex-shrink-0">
<div class="flex-shrink-0 text-blue-100 dark:text-highlight-blue">
{@html getIconForRoute(item.route)}
</div>
@@ -172,7 +225,9 @@
{/if}
{#if $page.url.pathname === item.route}
<div class="absolute left-0 top-0 bottom-0 w-1 bg-white dark:bg-blue-400"></div>
<div
class="absolute left-0 top-0 bottom-0 w-1 bg-white dark:bg-highlight-blue"
></div>
{/if}
</a>
{/each}
@@ -182,13 +237,13 @@
<a
class="flex items-center px-3 py-2 text-sm transition-all duration-150 relative group
{$page.url.pathname === link.route
? 'text-white font-medium bg-active-blue dark:bg-indigo-600 shadow-md'
: 'text-blue-100 dark:text-gray-200 hover:text-white dark:hover:text-gray-100 dark:hover:bg-gray-700'}"
? 'text-white font-medium bg-active-blue dark:bg-active-blue shadow-md'
: 'text-blue-100 dark:text-gray-200 hover:text-white dark:hover:text-gray-100 hover:bg-highlight-blue/80 dark:hover:bg-highlight-blue/20'}"
draggable="false"
href={link.route}
>
<!-- Icon -->
<div class="flex-shrink-0">
<div class="flex-shrink-0 text-blue-100 dark:text-highlight-blue">
{@html icons[link.label]}
</div>
@@ -196,16 +251,16 @@
<span class="ml-3 truncate">{link.label}</span>
{:else}
<div
class="absolute left-14 rounded bg-gray-900 dark:bg-gray-800 text-white dark:text-gray-100 px-2 py-1 ml-6 text-sm
class="absolute left-14 rounded bg-gray-900 dark:bg-gray-800 dark:border-highlight-blue/40 text-white dark:text-highlight-blue px-2 py-1 ml-6 text-sm
invisible opacity-0 -translate-x-3 group-hover:visible group-hover:opacity-100 group-hover:translate-x-0
transition-all duration-150 whitespace-nowrap z-50 shadow-lg border dark:border-gray-600"
transition-all duration-150 whitespace-nowrap z-50 shadow-lg border dark:border-highlight-blue/40"
>
{link.label}
</div>
{/if}
{#if $page.url.pathname === link.route}
<div class="absolute left-0 top-0 bottom-0 w-1 bg-white dark:bg-blue-400"></div>
<div class="absolute left-0 top-0 bottom-0 w-1 bg-white dark:bg-highlight-blue"></div>
{/if}
</a>
{/if}
@@ -3,6 +3,7 @@
import { onMount } from 'svelte';
import Logo from './Logo.svelte';
import ThemeToggle from '../ThemeToggle.svelte';
import CompanyBanner from './CompanyBanner.svelte';
const appState = AppStateService.instance;
@@ -64,12 +65,12 @@
}
function profilePattern(username) {
// Create consistent hash
// create consistent hash
const hash = username.split('').reduce((acc, char) => {
return char.charCodeAt(0) + ((acc << 5) - acc);
}, 0);
// Generate base colors
// generate base colors
const hue = Math.abs(hash) % 360;
const colors = {
primary: `hsl(${hue}, 70%, 50%)`,
@@ -77,7 +78,7 @@
accent: `hsl(${(hue + 240) % 360}, 70%, 50%)`
};
// Generate pattern parameters
// generate pattern parameters
const params = {
rotation: hash % 360,
segments: 6 + (hash % 6),
@@ -113,91 +114,77 @@
$: initials = getInitials(username || 'U');
</script>
<div
class="header-container sticky top-0 z-20 col-span-12 h-16 bg-pc-darkblue dark:bg-gray-800 border-b border-pc-darkblue/20 dark:border-gray-700 flex justify-between items-center"
>
<Logo />
{#if isInstalled}
<div class="hidden lg:flex flex-row items-center px-8 h-full justify-self-end">
{#if context.current === AppStateService.CONTEXT.COMPANY}
<p class="text-slate-300 dark:text-gray-300 uppercase font-bold text-lg mr-4">
{context.companyName}
</p>
{/if}
<button
class="rounded-md h-3/4 px-8 text-white bg-indigo-500 hover:bg-cta-blue dark:bg-indigo-600 dark:hover:bg-indigo-700 uppercase font-semibold mr-4 transition-colors duration-200"
on:click={toggleChangeCompanyModal}
>
Change company
</button>
<div class="sticky top-0 z-20">
<CompanyBanner />
<div
class="header-container col-span-12 h-16 bg-pc-darkblue dark:bg-gray-800 border-b border-pc-darkblue/20 dark:border-highlight-blue/30 flex justify-between items-center"
>
<Logo />
{#if isInstalled}
<div class="hidden lg:flex flex-row items-center px-8 h-full justify-self-end">
{#if isUpdateAvailable}
<a
class="flex items-center gap-2 mr-8 text-lg font-medium text-white bg-gradient-to-r from-indigo-500 to-purple-500 dark:from-active-blue dark:to-highlight-blue rounded-md px-4 py-2 transition-all duration-300 transform hover:-translate-y-0.5 focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:ring-offset-2 dark:focus:ring-offset-gray-800 active:scale-95 fixed bottom-4 right-2 shadow-md shadow-black dark:shadow-gray-900"
href={'/settings/update'}
>
<span class=""></span>
<span>Update Available</span>
</a>
{/if}
<div class="relative ml-10 flex items-center gap-4">
<ThemeToggle />
<button
id="toggle-profile-menu"
class="group flex items-center"
on:click={() => (isProfileMenuVisible = !isProfileMenuVisible)}
>
<!-- main circle with initials -->
<div
class="w-10 h-10 rounded-full bg-cta-blue hover:bg-cta-blue/80 dark:bg-active-blue dark:hover:bg-active-blue/80 dark:border dark:border-highlight-blue/40 flex items-center justify-center text-white font-medium relative transition-colors duration-200"
>
{initials}
{#if isUpdateAvailable}
<a
class="flex items-center gap-2 mr-8 text-lg font-medium text-white bg-gradient-to-r from-indigo-500 to-purple-500 dark:from-indigo-600 dark:to-purple-600 rounded-md px-4 py-2 transition-all duration-300 transform hover:-translate-y-0.5 focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:ring-offset-2 dark:focus:ring-offset-gray-800 active:scale-95 fixed bottom-4 right-2 shadow-md shadow-black dark:shadow-gray-900"
href={'/settings/update'}
<div class="absolute -bottom-1 -right-1 w-5 h-5"></div>
</div>
<!-- dropdown indicator -->
<svg
class="w-4 h-4 ml-2 text-gray-300 dark:text-highlight-blue transition-transform duration-200 group-hover:text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 9l-7 7-7-7"
/>
</svg>
</button>
</div>
</div>
<div class="flex lg:hidden items-center mr-4 gap-4">
<div
class="flex items-center justify-center w-10 h-10 rounded-lg hover:bg-white/10 dark:hover:bg-white/10 transition-colors duration-200"
>
<span class=""></span>
<span>Update Available</span>
</a>
{/if}
<div class="relative ml-10 flex items-center gap-4">
<ThemeToggle />
<ThemeToggle />
</div>
<button
id="toggle-profile-menu"
class="group flex items-center"
on:click={() => (isProfileMenuVisible = !isProfileMenuVisible)}
class="flex items-center justify-center w-10 h-10 rounded-lg hover:bg-white/10 dark:hover:bg-white/10 transition-colors duration-200"
on:click={() => (isMobileMenuVisible = !isMobileMenuVisible)}
>
<!-- Main Circle with Initials -->
<div
class="w-10 h-10 rounded-full bg-cta-blue hover:bg-indigo-500 dark:bg-indigo-600 dark:hover:bg-indigo-700 flex items-center justify-center text-white font-medium relative transition-colors duration-200"
>
{initials}
<div class="absolute -bottom-1 -right-1 w-5 h-5"></div>
</div>
<!-- Dropdown Indicator -->
<svg
class="w-4 h-4 ml-2 text-gray-300 dark:text-gray-400 transition-transform duration-200 group-hover:text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 9l-7 7-7-7"
/>
</svg>
<img class="w-6 h-6" src="/mob-menu-button.svg" alt="toggle mobile menu" />
</button>
</div>
</div>
<div class="flex lg:hidden items-center mr-4 gap-4">
<div
class="flex items-center justify-center w-10 h-10 rounded-lg hover:bg-white/10 transition-colors duration-200"
>
<ThemeToggle />
</div>
<button
class="rounded-md px-3 py-2 text-white bg-indigo-500 hover:bg-cta-blue dark:bg-indigo-600 dark:hover:bg-indigo-700 uppercase font-semibold text-xs transition-colors duration-200"
on:click={toggleChangeCompanyModal}
>
Change company
</button>
<button
class="flex items-center justify-center w-10 h-10 rounded-lg hover:bg-white/10 transition-colors duration-200"
on:click={() => (isMobileMenuVisible = !isMobileMenuVisible)}
>
<img class="w-6 h-6" src="/mob-menu-button.svg" alt="toggle mobile menu" />
</button>
</div>
{/if}
{/if}
</div>
</div>
<style>
/* Prevent any hover effects on the header */
/* prevent any hover effects on the header */
.header-container {
background-color: #0b2063 !important;
}
@@ -6,8 +6,142 @@
import ThemeToggle from '../ThemeToggle.svelte';
export let visible = false;
export let username = '';
export let onClickLogout;
export let toggleChangeCompanyModal;
const icons = {
dashboard: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
</svg>`,
campaigns_overview: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M8 5h6c2 0 4 2 4 4v4c0 3-3 5-5 5 1.5-1.5 1.5-3 1.5-3" />
<path stroke-linecap="round" stroke-linejoin="round" d="M14.5 15l-2 2" />
<circle cx="14" cy="5" r="1" fill="currentColor" />
</svg>`,
campaign_templates: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" />
</svg>`,
ip_filters: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 0 1-.659 1.591l-5.432 5.432a2.25 2.25 0 0 0-.659 1.591v2.927a2.25 2.25 0 0 1-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 0 0-.659-1.591L3.659 7.409A2.25 2.25 0 0 1 3 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0 1 12 3Z" />
</svg>`,
webhooks: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z" />
</svg>`,
recipients_overview: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
</svg>`,
recipient_groups: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z" />
</svg>`,
domains_overview: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418" />
</svg>`,
pages: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" />
</svg>`,
assets: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" />
</svg>`,
emails_overview: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75" />
</svg>`,
attachments: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
</svg>`,
smtp_configurations: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 12a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0Zm0 0c0 1.657 1.007 3 2.25 3S21 13.657 21 12a9 9 0 1 0-2.636 6.364M16.5 12V8.25" />
</svg>`,
api_senders: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5" />
</svg>`,
proxy: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M7.5 21 3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5" />
</svg>`,
// Top menu icons
profile: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
</svg>`,
sessions: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>`,
users: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z" />
</svg>`,
companies: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
</svg>`,
settings: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a6.759 6.759 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>`,
'user guide': `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" />
</svg>`,
'change company': `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" />
</svg>`
};
const getIconForRoute = (route) => {
const iconMap = {
'/dashboard/': 'dashboard',
'/campaign/': 'campaigns_overview',
'/campaign-template/': 'campaign_templates',
'/ip-filter/': 'ip_filters',
'/webhook/': 'webhooks',
'/recipient/': 'recipients_overview',
'/recipient/group/': 'recipient_groups',
'/domain/': 'domains_overview',
'/page/': 'pages',
'/proxy/': 'proxy',
'/asset/': 'assets',
'/email/': 'emails_overview',
'/attachment/': 'attachments',
'/smtp-configuration/': 'smtp_configurations',
'/api-sender/': 'api_senders',
'/profile/': 'profile',
'/sessions/': 'sessions',
'/user/': 'users',
'/company/': 'companies',
'/settings/': 'settings'
};
return icons[iconMap[route] || 'dashboard'];
};
const getTopMenuIcon = (label) => {
const iconMap = {
Profile: 'profile',
Sessions: 'sessions',
Users: 'users',
Companies: 'companies',
Settings: 'settings',
'User Guide': 'user guide',
'Change Company': 'change company'
};
return icons[iconMap[label] || 'profile'];
};
</script>
{#if visible}
@@ -20,21 +154,21 @@
<!-- Mobile Menu -->
<div
class="mobile-menu-content fixed top-0 left-0 w-full h-full bg-pc-darkblue dark:bg-gray-900 z-50 overflow-y-auto shadow-xl transition-colors duration-200"
class="mobile-menu-content fixed top-0 left-0 w-full h-full bg-gradient-to-b from-pc-darkblue to-slate-900 dark:from-gray-900 dark:to-gray-950 z-50 overflow-y-auto shadow-xl"
>
<!-- Header -->
<div
class="mobile-menu-header flex justify-between h-20 items-center bg-pc-darkblue dark:bg-gray-800 px-6"
class="mobile-menu-header flex justify-between h-20 items-center bg-pc-darkblue/90 dark:bg-gray-900/90 backdrop-blur-sm px-6 border-b border-white/10 dark:border-gray-700/50"
>
<img class="w-40 h-auto" src="/logo-white.svg" alt="logo" />
<div class="flex items-center gap-4">
<div class="flex items-center gap-2">
<div
class="flex items-center justify-center w-12 h-12 rounded-lg hover:bg-white/10 dark:hover:bg-gray-600/30 transition-colors duration-200"
class="flex items-center justify-center w-12 h-12 rounded-lg hover:bg-white/10 dark:hover:bg-gray-600/30 transition-all duration-200"
>
<ThemeToggle />
</div>
<button
class="flex items-center justify-center w-12 h-12 rounded-lg hover:bg-white/10 dark:hover:bg-gray-600/30 transition-colors duration-200"
class="flex items-center justify-center w-12 h-12 rounded-lg hover:bg-white/10 dark:hover:bg-gray-600/30 transition-all duration-200"
on:click={() => (visible = false)}
>
<img class="w-6 h-6" src="/mob-menu-close.svg" alt="close mobile menu" />
@@ -42,110 +176,153 @@
</div>
</div>
<!-- User Section -->
<div class="p-6 border-b border-white dark:border-gray-700">
<h1 class="font-bold text-xl text-white dark:text-gray-100 mb-4">
{username ?? ''}
</h1>
<!-- Switch Company Section -->
<div class="px-6 py-4 border-b border-white/20 dark:border-highlight-blue/30">
<button
on:click={onClickLogout}
class="bg-cta-blue dark:bg-indigo-600 dark:hover:bg-indigo-700 uppercase font-bold py-3 px-6 rounded-md transition-colors duration-200 text-sm text-white"
class="flex items-center w-full py-3 px-4 text-white bg-active-blue dark:bg-highlight-blue/30 rounded-lg transition-all duration-200 hover:bg-active-blue/80 dark:hover:bg-highlight-blue/40 font-medium"
on:click={() => {
visible = false;
toggleChangeCompanyModal();
}}
>
Log Out
<div class="flex-shrink-0 mr-3 text-white dark:text-highlight-blue">
{@html getTopMenuIcon('Change Company')}
</div>
<span class="flex-1 text-left">Switch Company</span>
</button>
</div>
<!-- Top Menu -->
<div class="p-4">
<div
class="bg-gradient-to-b from-cta-blue to-indigo-500 dark:from-gray-800 dark:to-gray-700 rounded-md"
<!-- Quick Actions Section -->
<div class="p-6 border-b border-white/20 dark:border-highlight-blue/30">
<h2
class="text-white/80 dark:text-highlight-blue text-lg font-semibold uppercase tracking-wider mb-4 text-left"
>
Quick Access
</h2>
<div class="space-y-2">
{#each mobileTopMenu as link}
<a
class="block text-center py-4 text-white text-lg font-medium first:rounded-t-md last:rounded-b-md transition-colors duration-200"
class:bg-active-blue={$page.url.pathname === link.route}
class:dark:bg-gray-700={$page.url.pathname === link.route}
class:shadow-md={$page.url.pathname === link.route}
class="flex items-center w-full py-3 px-4 text-white text-lg font-medium rounded-lg transition-all duration-200 group {$page
.url.pathname === link.route
? 'bg-active-blue shadow-lg dark:bg-active-blue'
: 'hover:bg-highlight-blue/30 hover:shadow-md dark:hover:bg-highlight-blue/20'}"
class:hidden={shouldHideMenuItem(link.route)}
on:click={() => (visible = false)}
target={link.external ? '_blank' : '_self'}
href={link.route}
>
{link.label}
<div class="flex-shrink-0 mr-3 text-white dark:text-highlight-blue">
{@html getTopMenuIcon(link.label)}
</div>
<span class="flex-1 text-left">{link.label}</span>
{#if $page.url.pathname === link.route}
<div class="w-2 h-2 bg-white rounded-full"></div>
{/if}
</a>
{/each}
</div>
</div>
<!-- Main Menu -->
<div class="p-4 pt-0">
<div
class="bg-gradient-to-b from-cta-blue to-indigo-500 dark:from-gray-800 dark:to-gray-700 rounded-md"
<!-- Main Navigation -->
<div class="p-6">
<h2
class="text-white/80 dark:text-highlight-blue text-lg font-semibold uppercase tracking-wider mb-4 text-left"
>
Navigation
</h2>
<div class="space-y-1">
{#each menu as link}
{#if link.type === 'submenu'}
<div
class="text-center py-4 text-white font-semibold text-lg border-b border-white/20 dark:border-gray-600"
>
{link.label}
<!-- section header -->
<div class="pt-4 pb-2 first:pt-0">
<div class="py-2 px-4 border-l-2 border-cta-blue/60 dark:border-highlight-blue/60">
<h3
class="text-white dark:text-highlight-blue font-bold text-lg uppercase tracking-wide text-left"
>
{link.label}
</h3>
</div>
</div>
{#each link.items as item, i (i)}
<!-- submenu items -->
<div class="ml-4 space-y-1">
{#each link.items as item, i (i)}
<a
class="flex items-center w-full py-2.5 px-4 text-white/90 dark:text-gray-300 text-base font-medium rounded-lg transition-all duration-200 group hover:bg-highlight-blue/30 dark:hover:bg-highlight-blue/20 hover:text-white dark:hover:text-white"
href={item.route}
on:click={() => (visible = false)}
>
<div
class="flex-shrink-0 mr-3 text-white/70 dark:text-highlight-blue/80 group-hover:text-white"
>
{@html getIconForRoute(item.route)}
</div>
<span class="text-left">
{#if i === 0}
Overview
{:else if item.singleLabel}
{item.singleLabel}
{:else}
{item.label}
{/if}
</span>
</a>
{/each}
</div>
{:else}
<!-- standalone menu item -->
<div class="pt-4 pb-2 first:pt-0">
<a
class="block text-center py-3 text-white text-base transition-colors duration-200"
class:last:rounded-b-md={i === link.items.length - 1}
href={item.route}
class="flex items-center w-full py-3 px-4 text-white text-lg font-bold rounded-lg transition-all duration-200 group hover:bg-highlight-blue/30 dark:hover:bg-highlight-blue/20"
href={link.route}
on:click={() => (visible = false)}
>
{#if i === 0}
Overview
{:else if item.singleLabel}
{item.singleLabel}
{:else}
{item.label}
{/if}
<div class="flex-shrink-0 mr-3 text-white dark:text-highlight-blue">
{@html getIconForRoute(link.route)}
</div>
<span class="text-left">{link.label}</span>
</a>
{/each}
{:else}
<a
class="block text-center py-4 text-white text-lg font-medium transition-colors duration-200"
href={link.route}
on:click={() => (visible = false)}
>
{link.label}
</a>
</div>
{/if}
{/each}
</div>
</div>
<!-- Logout Section -->
<div class="p-6 border-t border-white/20 dark:border-highlight-blue/30 mt-auto">
<button
on:click={onClickLogout}
class="w-full border-2 border-white/30 dark:border-highlight-blue/50 hover:border-highlight-blue/60 dark:hover:border-highlight-blue hover:bg-highlight-blue/20 dark:hover:bg-highlight-blue/20 uppercase font-medium py-2.5 px-6 rounded-lg transition-all duration-200 text-sm text-white/90 dark:text-highlight-blue hover:text-white dark:hover:text-white"
>
Log Out
</button>
</div>
<!-- Footer spacer -->
<div class="h-6"></div>
</div>
{/if}
<style>
/* Prevent any hover effects on the mobile menu header */
:global(.mobile-menu-header) {
background-color: #0b2063 !important;
}
:global(.mobile-menu-header:hover) {
background-color: #0b2063 !important;
}
:global(.dark .mobile-menu-header) {
background-color: #1f2937 !important;
}
:global(.dark .mobile-menu-header:hover) {
background-color: #1f2937 !important;
/* ensure consistent background colors */
.mobile-menu-content {
background: linear-gradient(to bottom, #0b2063, #1e293b);
}
/* Prevent any hover effects on the mobile menu content */
.mobile-menu-content {
background-color: #0b2063 !important;
}
.mobile-menu-content:hover {
background-color: #0b2063 !important;
}
:global(.dark) .mobile-menu-content {
background-color: #111827 !important;
background: linear-gradient(to bottom, #111827, #0f172a);
}
:global(.dark) .mobile-menu-content:hover {
background-color: #111827 !important;
/* smooth scrolling */
.mobile-menu-content {
scroll-behavior: smooth;
}
/* hide scrollbar but keep functionality */
.mobile-menu-content::-webkit-scrollbar {
display: none;
}
.mobile-menu-content {
-ms-overflow-style: none;
scrollbar-width: none;
}
</style>
@@ -5,6 +5,52 @@
import { shouldHideMenuItem } from '$lib/utils/common';
export let logout;
export let visible = false;
export let toggleChangeCompanyModal;
const icons = {
profile: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
</svg>`,
sessions: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>`,
users: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z" />
</svg>`,
companies: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
</svg>`,
settings: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a6.759 6.759 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>`,
'user guide': `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" />
</svg>`,
'change company': `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" />
</svg>`
};
const getTopMenuIcon = (label) => {
const iconMap = {
Profile: 'profile',
Sessions: 'sessions',
Users: 'users',
Companies: 'companies',
Settings: 'settings',
'User Guide': 'user guide',
'Change Company': 'change company'
};
return icons[iconMap[label] || 'profile'];
};
const handleClickOutsideNavigation = (event) => {
const profileMenuElement = document.getElementById('profile-menu');
@@ -42,32 +88,48 @@
class="lg:flex flex-col h-fit lg:col-start-10 lg:col-span-3 row-start-1 xl:col-start-11 xl:col-span-2 2xl:col-start-11 2xl:col-span-2 sticky top-20 z-30"
>
<div
class="flex flex-col bg-gradient-to-b from-cta-blue to-indigo-500 dark:from-gray-800 dark:to-gray-700 rounded-md transition-colors duration-200"
class="flex flex-col bg-gradient-to-b from-cta-blue to-indigo-500 dark:from-gray-800 dark:to-gray-700 rounded-md transition-colors duration-200 dark:border dark:border-highlight-blue/30"
in:fadeIn={{ duration: 150 }}
>
<!-- Change Company at top with distinct styling -->
<button
class="flex items-center pl-5 py-3 text-white bg-active-blue dark:bg-highlight-blue/30 first:rounded-t-md transition-colors duration-200 hover:bg-active-blue/80 dark:hover:bg-highlight-blue/40 border-b border-active-blue/50 dark:border-highlight-blue/40"
on:click={() => {
visible = false;
toggleChangeCompanyModal();
}}
>
<div class="flex-shrink-0 mr-3 text-white dark:text-highlight-blue">
{@html getTopMenuIcon('Change Company')}
</div>
<span class="font-medium">Switch Company</span>
</button>
{#each topMenu as item}
<a
class="pl-5 py-2 text-white last:rounded-md first:rounded-t-md transition-colors duration-200"
class:hover:shadow-md={$page.url.pathname !== item.route}
class:hover:bg-highlight-blue={$page.url.pathname !== item.route}
class:dark:hover:bg-gray-600={$page.url.pathname !== item.route}
class:bg-active-blue={$page.url.pathname === item.route}
class:dark:bg-gray-700={$page.url.pathname === item.route}
class:shadow-md={$page.url.pathname === item.route}
class="flex items-center pl-5 py-2 text-white last:rounded-md first:rounded-t-md transition-colors duration-200 {$page
.url.pathname === item.route
? 'bg-active-blue shadow-md dark:bg-active-blue'
: 'hover:shadow-md hover:bg-highlight-blue/80 dark:hover:bg-highlight-blue/20'}"
class:hidden={shouldHideMenuItem(item.route)}
target={item.external ? '_blank' : '_self'}
draggable="false"
on:click={() => {
visible = false;
}}
href={item.route}>{item.label}</a
href={item.route}
>
<div class="flex-shrink-0 mr-3 text-white dark:text-highlight-blue">
{@html getTopMenuIcon(item.label)}
</div>
<span>{item.label}</span>
</a>
{/each}
<button
on:click={logout}
class="bg-white dark:bg-gray-800 uppercase font-bold hover:bg-pc-lightblue dark:hover:bg-gray-700 py-2 mx-4 my-4 rounded-md transition-colors duration-200"
class="bg-white dark:bg-gray-800 dark:border dark:border-highlight-blue/40 uppercase font-bold hover:bg-cta-blue/10 dark:hover:bg-highlight-blue/20 py-2 mx-4 my-4 rounded-md transition-colors duration-200"
>
<p class="text-cta-blue dark:text-gray-100 py px-8 transition-colors duration-200">
<p class="text-cta-blue dark:text-highlight-blue py px-8 transition-colors duration-200">
Log Out
</p>
</button>
@@ -117,21 +117,21 @@
<div class="flex-grow p-6">
{#if isLoadingCompanies}
<div class="flex items-center justify-center py-8">
<div class="text-gray-500 dark:text-gray-400 transition-colors duration-200">
<div class="text-gray-500 dark:text-gray-500 transition-colors duration-200">
Loading companies...
</div>
</div>
{:else if companies.length === 0}
<div class="flex flex-col items-center justify-center py-8 text-center">
<div class="text-gray-500 dark:text-gray-400 mb-4 transition-colors duration-200">
<div class="text-gray-500 dark:text-gray-500 mb-4 transition-colors duration-200">
No companies found.
</div>
<div class="text-sm text-gray-400 dark:text-gray-500 mb-4 transition-colors duration-200">
<div class="text-sm text-gray-400 dark:text-gray-600 mb-4 transition-colors duration-200">
You need to create a company first before you can switch to it.
</div>
<a
href="/company/"
class="bg-cta-blue dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 text-sm uppercase font-bold px-4 py-2 text-white rounded-md transition-colors duration-200"
class="bg-cta-blue dark:bg-highlight-blue/80 hover:bg-blue-700 dark:hover:bg-highlight-blue hover:text-white text-sm uppercase font-bold px-4 py-2 text-white rounded-md transition-colors duration-200"
on:click={() => {
visible = false;
}}
@@ -148,12 +148,12 @@
<!-- Button Section -->
<div
class="border-t border-gray-200 dark:border-gray-600 p-6 mt-36 flex flex-wrap gap-4 justify-end transition-colors duration-200"
class="border-t border-gray-200 dark:border-gray-700/60 p-6 mt-36 flex flex-wrap gap-4 justify-end transition-colors duration-200"
>
{#if inContext}
<button
type="button"
class="bg-slate-400 dark:bg-gray-600 hover:bg-slate-300 dark:hover:bg-gray-500 text-sm mr-2 uppercase font-bold px-4 py-2 text-white rounded-md disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200"
class="bg-slate-400 dark:bg-gray-700/80 hover:bg-slate-300 dark:hover:bg-gray-600/80 text-sm mr-2 uppercase font-bold px-4 py-2 text-white rounded-md disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200"
disabled={isLoadingCompanies}
on:click={onClickSwitchToAdministratorContext}
>
@@ -163,7 +163,7 @@
<button
type="submit"
class="bg-cta-blue dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 text-sm uppercase font-bold px-4 py-2 text-white rounded-md disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200"
class="bg-cta-blue dark:bg-highlight-blue/80 hover:bg-blue-700 dark:hover:bg-highlight-blue hover:text-white text-sm uppercase font-bold px-4 py-2 text-white rounded-md disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200"
disabled={isLoadingCompanies || !selectedCompany}
on:click={onClickSwitch}
>
@@ -46,20 +46,20 @@
<!--
<h3 class="text-lg font-medium text-gray-900">Delete {type}</h3>
-->
<p class="mt-2 text-gray-600 dark:text-gray-300">
<p class="mt-2 text-gray-600 dark:text-gray-400">
Are you sure you want to delete
{#if name?.length > 30}
<br />
{/if}
<span class="font-medium text-gray-900 dark:text-gray-100">"{name}"</span>?
<span class="font-medium text-gray-900 dark:text-gray-300">"{name}"</span>?
</p>
</div>
<!-- Impact Section -->
{#if list.length}
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4 transition-colors duration-200">
<p class="font-medium text-gray-900 dark:text-gray-100 mb-3">Side effects:</p>
<ul class="space-y-2 ml-4 list-disc text-gray-600 dark:text-gray-300">
<div class="bg-gray-50 dark:bg-gray-800/40 rounded-lg p-4 transition-colors duration-200">
<p class="font-medium text-gray-900 dark:text-gray-300 mb-3">Side effects:</p>
<ul class="space-y-2 ml-4 list-disc text-gray-600 dark:text-gray-400">
{#each list as line}
<li>{line}</li>
{/each}
@@ -85,7 +85,7 @@
{/if}
{#if permanent}
<p class="text-red-700 dark:text-red-400 font-medium">This action cannot be undone.</p>
<p class="text-red-700 dark:text-red-300 font-medium">This action cannot be undone.</p>
{/if}
</div>
</Alert>
@@ -5,7 +5,7 @@
</script>
<button
class="hover:bg-gray-100 dark:hover:bg-gray-700 px-2 py-1 rounded-md transition-colors w-full text-left text-ellipsis overflow-hidden text-gray-900 dark:text-gray-100"
class="hover:bg-gray-100 dark:hover:bg-gray-800/40 px-2 py-1 rounded-md transition-colors w-full text-left text-ellipsis overflow-hidden text-gray-900 dark:text-gray-200"
title={text}
on:click={() => onClickCopy(text)}
>
@@ -5,14 +5,14 @@
export let colspan = 100;
</script>
<tr class="text-center bg-pleasant-gray dark:bg-gray-700 transition-colors duration-200">
<tr class="text-center bg-pleasant-gray dark:bg-gray-800/40 transition-colors duration-200">
<td class="p-24" {colspan}>
{#if page === 1}
<p class="text-lg text-gray-600 dark:text-gray-300 transition-colors duration-200">
<p class="text-lg text-gray-600 dark:text-gray-200 transition-colors duration-200">
No {plural} found
</p>
{:else}
<p class="text-lg text-gray-600 dark:text-gray-300 transition-colors duration-200">
<p class="text-lg text-gray-600 dark:text-gray-200 transition-colors duration-200">
No more results found
</p>
{/if}
@@ -11,7 +11,7 @@
</script>
<div
class="flex items-center text-ellipsis text-gray-900 dark:text-gray-100 transition-colors duration-200"
class="flex items-center text-ellipsis text-gray-900 dark:text-gray-200 transition-colors duration-200"
>
<div class="w-4 h-4 {event.color} mr-2 rounded-sm"></div>
{event.name}
@@ -56,11 +56,11 @@
<div
bind:this={tableWrapper}
class="
border-2 border-gray-200 dark:border-gray-600 rounded-md px-4 py-4 overflow-x-auto bg-white dark:bg-gray-800 transition-colors duration-200
border-2 border-gray-200 dark:border-gray-700/60 rounded-md px-4 py-4 overflow-x-auto bg-white dark:bg-gray-900/80 transition-colors duration-200
{scrollBarClassesHorizontal}"
>
<table
class="w-full table-fixed bg-white dark:bg-gray-800 transition-colors duration-200"
class="w-full table-fixed bg-white dark:bg-gray-900/80 transition-colors duration-200"
class:animate-pulse={isGhost}
>
<TableHeader {isGhost} {columns} {sortable} {hasActions} {pagination} />
@@ -11,7 +11,7 @@
</script>
<td
class={`pl-4 font-regular text-slate-600 dark:text-gray-300 text-ellipsis whitespace-nowrap overflow-hidden pr-4 transition-colors duration-200`}
class={`pl-4 font-regular text-slate-600 dark:text-gray-200 text-ellipsis whitespace-nowrap overflow-hidden pr-4 transition-colors duration-200`}
title={isDate ? '' : value}
>
{#if value}
@@ -1,5 +1,5 @@
<td class="w-48 text-center border: hidden;">
<p class="font-regular text-slate-600 dark:text-gray-300 transition-colors duration-200">
<p class="font-regular text-slate-600 dark:text-gray-200 transition-colors duration-200">
<slot />
</p>
</td>
@@ -4,7 +4,7 @@
<td class="pl-4 w-40 text-center border: hidden;">
<p
class="font-regular text-slate-600 dark:text-gray-300 flex justify-center transition-colors duration-200"
class="font-regular text-slate-600 dark:text-gray-200 flex justify-center transition-colors duration-200"
>
{value ? 'Yes' : 'No'}
</p>
@@ -1 +1,2 @@
<td class="pl-4 w-4 border: hidden; bg-white dark:bg-gray-800 transition-colors duration-200"> </td>
<td class="pl-4 w-4 border: hidden; bg-white dark:bg-gray-900/40 transition-colors duration-200">
</td>
@@ -4,10 +4,10 @@
</script>
<td
class="pl-4 font-regular text-slate-600 dark:text-gray-300 text-ellipsis whitespace-nowrap overflow-hidden pr-4 transition-colors duration-200"
class="pl-4 font-regular text-slate-600 dark:text-gray-200 text-ellipsis whitespace-nowrap overflow-hidden pr-4 transition-colors duration-200"
{title}
>
<a href={href} class="block w-full py-1">
<a {href} class="block w-full py-1">
<slot />
</a>
</td>
@@ -9,7 +9,7 @@
{#if disabled}
<button
class="w-full px py text-slate-300 dark:text-gray-500 cursor-not-allowed text-left transition-colors duration-200"
class="w-full px py text-slate-300 dark:text-gray-600 cursor-not-allowed text-left transition-colors duration-200"
{disabled}
{title}
>
@@ -17,7 +17,7 @@
</button>
{:else}
<button
class="w-full px py-1 text-slate-600 dark:text-gray-300 hover:bg-red-400 dark:hover:bg-red-500 hover:text-white cursor-pointer text-left transition-colors duration-200"
class="w-full px py-1 text-slate-600 dark:text-gray-200 hover:bg-red-400 dark:hover:bg-red-600/70 hover:text-white cursor-pointer text-left transition-colors duration-200"
on:click
{title}
>
@@ -17,7 +17,7 @@
{#if disabled}
<button
class="w-full px py-1 text-slate-300 dark:text-gray-500 cursor-not-allowed text-left transition-colors duration-200"
class="w-full px py-1 text-slate-300 dark:text-gray-600 cursor-not-allowed text-left transition-colors duration-200"
{disabled}
{title}
>
@@ -25,7 +25,7 @@
</button>
{:else}
<button
class="w-full px py-1 text-slate-600 dark:text-gray-300 hover:bg-highlight-blue dark:hover:bg-blue-600 hover:text-white cursor-pointer text-left transition-colors duration-200"
class="w-full px py-1 text-slate-600 dark:text-gray-200 hover:bg-highlight-blue dark:hover:bg-highlight-blue/50 hover:text-white cursor-pointer text-left transition-colors duration-200"
on:click
on:keydown={handleKeydown}
{title}
@@ -160,7 +160,7 @@
<div
bind:this={menuRef}
class="absolute bg-white dark:bg-gray-800 drop-shadow-md dark:shadow-gray-900/50 border dark:border-gray-600 z-20 w-48 rounded-md overflow-y-scroll transition-colors duration-200 {scrollBarClassesVertical}"
class="absolute bg-white dark:bg-gray-900/90 drop-shadow-md dark:shadow-gray-900/50 border dark:border-gray-700/60 z-20 w-48 rounded-md overflow-y-scroll transition-colors duration-200 {scrollBarClassesVertical}"
class:hidden={!isMenuVisible}
>
<ul class="flex flex-col text-left">
@@ -1,3 +1,3 @@
<thead class="table-header-group bg-white dark:bg-gray-800 transition-colors duration-200">
<thead class="table-header-group bg-white dark:bg-gray-900/60 transition-colors duration-200">
<slot />
</thead>
@@ -34,7 +34,7 @@
</script>
<th
class="pl-4 bg-grayblue-light dark:bg-gray-700 py-4 border-hidden first:rounded-tl-lg first:rounded-bl-lg last:rounded-tr-lg last:border-4 min-w-48 transition-colors duration-200"
class="pl-4 bg-grayblue-light dark:bg-gray-800/60 py-4 border-hidden first:rounded-tl-lg first:rounded-bl-lg last:rounded-tr-lg last:border-4 min-w-48 transition-colors duration-200"
class:rounded-br-lg={last}
class:rounded-tr-lg={last}
class:w-48={size === 'small'}
@@ -43,9 +43,11 @@
class:w-full={fillRest}
>
<button
class="flex group cursor-pointer"
class="flex group cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700/30 rounded-md p-1 transition-colors duration-200"
class:pointer-events-none={!sortable}
class:table-cell={alignText === 'center'}
class:hover:bg-transparent={!sortable}
class:dark:hover:bg-transparent={!sortable}
on:click|preventDefault={setSortAndSortBy}
>
<div class="w-full">
@@ -60,7 +62,7 @@
{#if sortable && column.toLowerCase() === sortBy.toLowerCase() && !isGhost}
<div
class:bg-transparent={sortOrder === ''}
class="flex justify-center items-center w-6 h-6 ml-2 rounded-md bg-cta-blue dark:bg-blue-600 transition-colors duration-200"
class="flex justify-center items-center w-6 h-6 ml-2 rounded-md bg-cta-blue dark:bg-highlight-blue/80 transition-colors duration-200"
>
{#if sortOrder === 'asc'}
<div>
@@ -1,5 +1,5 @@
<th
class="bg-cta-light-blue dark:bg-gray-700 w-20 py-4 border-hidden rounded-lg transition-colors duration-200"
class="bg-cta-light-blue dark:bg-gray-800/60 w-20 py-4 border-hidden rounded-lg transition-colors duration-200"
>
<p class="font-bold text-slate-600 dark:text-gray-200 text-center transition-colors duration-200">
<slot />
@@ -1,5 +1,5 @@
<th
class="pl-4 bg-grayblue-light dark:bg-gray-700 w-48 py-4 border-hidden transition-colors duration-200"
class="pl-4 bg-grayblue-light dark:bg-gray-800/60 w-48 py-4 border-hidden transition-colors duration-200"
>
<p
class="font-bold text-slate-600 dark:text-gray-200 flex justify-center text-center transition-colors duration-200"
@@ -1,2 +1,2 @@
<th class="pl-4 bg-white dark:bg-gray-800 w-4 py-4 border-hidden transition-colors duration-200">
<th class="pl-4 bg-white dark:bg-gray-900 w-4 py-4 border-hidden transition-colors duration-200">
</th>
@@ -1,5 +1,5 @@
<th
class="pl-4 bg-grayblue-light dark:bg-gray-700 w-48 py-4 border-hidden rounded-md transition-colors duration-200"
class="pl-4 bg-grayblue-light dark:bg-gray-800/60 w-48 py-4 border-hidden rounded-md transition-colors duration-200"
>
<p class="font-bold text-slate-600 dark:text-gray-200 text-left transition-colors duration-200">
<slot />
@@ -1,5 +1,5 @@
<tr
class="table-row odd:bg-white odd:dark:bg-gray-800 even:bg-pleasant-gray even:dark:bg-gray-900 h-10 hover:bg-cta-light-blue hover:dark:bg-indigo-800 transition-colors duration-200"
class="table-row odd:bg-white odd:dark:bg-gray-900/40 even:bg-pleasant-gray even:dark:bg-gray-800/30 h-10 hover:bg-cta-light-blue hover:dark:bg-highlight-blue/20 transition-colors duration-200"
>
<slot />
</tr>
@@ -1,3 +1,3 @@
<tr class="table-row bg-white dark:bg-gray-800 h-4 transition-colors duration-200">
<tr class="table-row bg-white dark:bg-gray-900/40 h-4 transition-colors duration-200">
<slot />
</tr>
+3 -2
View File
@@ -17,6 +17,7 @@
import DesktopMenu from '$lib/components/header/DesktopMenu.svelte';
import { hideIsLoading, showIsLoading } from '$lib/store/loading';
import Header from '$lib/components/header/Header.svelte';
import CompanyBanner from '$lib/components/header/CompanyBanner.svelte';
import { setupTheme, setupOSThemeListener } from '$lib/theme.js';
// Removed feature flags import - no longer needed
@@ -222,10 +223,10 @@
{#if installState === AppStateService.INSTALL.INSTALLED}
<MobileMenu
bind:visible={isMobileMenuVisible}
username={user?.name}
onClickLogout={logout}
{toggleChangeCompanyModal}
/>
<ProfileMenu {logout} bind:visible={isProfileMenuVisible} />
<ProfileMenu {logout} bind:visible={isProfileMenuVisible} {toggleChangeCompanyModal} />
{/if}
<div class="col-start-1 col-end-13 row-start-1">
<slot />
@@ -945,21 +945,25 @@ Simulation URLs to allow:\n${allowListingData.simulationUrl}\n
<!-- Before Landing Page -->
<div class="flex items-center">
<div
class={`w-10 h-10 rounded-lg flex items-center justify-center border mr-3
${formValues.beforeLandingPage ? 'bg-blue-50 border-blue-300' : 'bg-gray-100 border-gray-300'}`}
class={`w-10 h-10 rounded-lg flex items-center justify-center border mr-3 transition-colors duration-200
${formValues.beforeLandingPage ? 'bg-blue-50 dark:bg-blue-900/40 border-blue-300 dark:border-blue-500' : 'bg-gray-100 dark:bg-gray-800/60 border-gray-300 dark:border-gray-600'}`}
>
<span
class={`text-xl ${formValues.beforeLandingPage ? 'text-blue-500' : 'text-gray-400'}`}
class={`text-xl transition-colors duration-200 ${formValues.beforeLandingPage ? 'text-blue-500 dark:text-blue-300' : 'text-gray-400 dark:text-gray-500'}`}
>1</span
>
</div>
<div class="flex-1">
<p class="text-xs font-medium">
<p
class="text-xs font-medium text-gray-900 dark:text-gray-300 transition-colors duration-200"
>
Before Landing {formValues.beforeLandingPageType === 'proxy'
? 'Proxy'
: 'Page'}
</p>
<p class="text-xs text-gray-500 truncate max-w-[180px]">
<p
class="text-xs text-gray-500 dark:text-gray-400 truncate max-w-[180px] transition-colors duration-200"
>
{formValues.beforeLandingPage || 'Not selected'}
</p>
</div>
@@ -967,21 +971,30 @@ Simulation URLs to allow:\n${allowListingData.simulationUrl}\n
<!-- Down Arrow -->
<div class="flex">
<div class="ml-5 w-0.5 h-4 bg-gray-300"></div>
<div
class="ml-5 w-0.5 h-4 bg-gray-300 dark:bg-gray-600 transition-colors duration-200"
></div>
</div>
<!-- Main Landing Page -->
<div class="flex items-center">
<div
class="w-10 h-10 rounded-lg bg-blue-100 flex items-center justify-center border border-blue-400 mr-3"
class="w-10 h-10 rounded-lg bg-blue-100 dark:bg-blue-900/40 flex items-center justify-center border border-blue-400 dark:border-blue-500 mr-3 transition-colors duration-200"
>
<span class="text-xl text-blue-600">2</span>
<span
class="text-xl text-blue-600 dark:text-blue-300 transition-colors duration-200"
>2</span
>
</div>
<div class="flex-1">
<p class="text-xs font-medium">
<p
class="text-xs font-medium text-gray-900 dark:text-gray-300 transition-colors duration-200"
>
Landing {formValues.landingPageType === 'proxy' ? 'Proxy' : 'Page'}
</p>
<p class="text-xs text-gray-500 truncate max-w-[180px]">
<p
class="text-xs text-gray-500 dark:text-gray-400 truncate max-w-[180px] transition-colors duration-200"
>
{formValues.landingPage || 'Required'}
</p>
</div>
@@ -989,25 +1002,31 @@ Simulation URLs to allow:\n${allowListingData.simulationUrl}\n
<!-- Down Arrow -->
<div class="flex">
<div class="ml-5 w-0.5 h-4 bg-gray-300"></div>
<div
class="ml-5 w-0.5 h-4 bg-gray-300 dark:bg-gray-600 transition-colors duration-200"
></div>
</div>
<!-- After Landing Page -->
<div class="flex items-center">
<div
class={`w-10 h-10 rounded-lg flex items-center justify-center border mr-3
${formValues.afterLandingPage ? 'bg-blue-50 border-blue-300' : 'bg-gray-100 border-gray-300'}`}
class={`w-10 h-10 rounded-lg flex items-center justify-center border mr-3 transition-colors duration-200
${formValues.afterLandingPage ? 'bg-blue-50 dark:bg-blue-900/40 border-blue-300 dark:border-blue-500' : 'bg-gray-100 dark:bg-gray-800/60 border-gray-300 dark:border-gray-600'}`}
>
<span
class={`text-xl ${formValues.afterLandingPage ? 'text-blue-500' : 'text-gray-400'}`}
class={`text-xl transition-colors duration-200 ${formValues.afterLandingPage ? 'text-blue-500 dark:text-blue-300' : 'text-gray-400 dark:text-gray-500'}`}
>3</span
>
</div>
<div class="flex-1">
<p class="text-xs font-medium">
<p
class="text-xs font-medium text-gray-900 dark:text-gray-300 transition-colors duration-200"
>
After Landing {formValues.afterLandingPageType === 'proxy' ? 'Proxy' : 'Page'}
</p>
<p class="text-xs text-gray-500 truncate max-w-[180px]">
<p
class="text-xs text-gray-500 dark:text-gray-400 truncate max-w-[180px] transition-colors duration-200"
>
{formValues.afterLandingPage || 'Not selected'}
</p>
</div>
@@ -1015,23 +1034,31 @@ Simulation URLs to allow:\n${allowListingData.simulationUrl}\n
<!-- Down Arrow -->
<div class="flex">
<div class="ml-5 w-0.5 h-4 bg-gray-300"></div>
<div
class="ml-5 w-0.5 h-4 bg-gray-300 dark:bg-gray-600 transition-colors duration-200"
></div>
</div>
<!-- Final Redirect -->
<div class="flex items-center">
<div
class={`w-10 h-10 rounded-lg flex items-center justify-center border mr-3
${formValues.afterLandingPageRedirectURL ? 'bg-blue-50 border-blue-300' : 'bg-gray-100 border-gray-300'}`}
class={`w-10 h-10 rounded-lg flex items-center justify-center border mr-3 transition-colors duration-200
${formValues.afterLandingPageRedirectURL ? 'bg-blue-50 dark:bg-blue-900/40 border-blue-300 dark:border-blue-500' : 'bg-gray-100 dark:bg-gray-800/60 border-gray-300 dark:border-gray-600'}`}
>
<span
class={`text-xl ${formValues.afterLandingPageRedirectURL ? 'text-blue-500' : 'text-gray-400'}`}
class={`text-xl transition-colors duration-200 ${formValues.afterLandingPageRedirectURL ? 'text-blue-500 dark:text-blue-300' : 'text-gray-400 dark:text-gray-500'}`}
>4</span
>
</div>
<div class="flex-1">
<p class="text-xs font-medium">POST Redirect URL</p>
<p class="text-xs text-gray-500 truncate max-w-[180px]">
<p
class="text-xs font-medium text-gray-900 dark:text-gray-300 transition-colors duration-200"
>
POST Redirect URL
</p>
<p
class="text-xs text-gray-500 dark:text-gray-400 truncate max-w-[180px] transition-colors duration-200"
>
{formValues.afterLandingPageRedirectURL || 'Not set'}
</p>
</div>
+8 -12
View File
@@ -6,12 +6,11 @@
import SubHeadline from '$lib/components/SubHeadline.svelte';
import { addToast } from '$lib/store/toast';
import { BiMap } from '$lib/utils/maps';
import { defaultOptions, fetchAllRows } from '$lib/utils/api-utils';
import { fetchAllRows } from '$lib/utils/api-utils';
import { AppStateService } from '$lib/service/appState';
import ProxySvgIcon from '$lib/components/ProxySvgIcon.svelte';
import TableRow from '$lib/components/table/TableRow.svelte';
import TableCell from '$lib/components/table/TableCell.svelte';
import TableCellLink from '$lib/components/table/TableCellLink.svelte';
import { formatWeekDays, formatTimeConstraint, timeFormat } from '$lib/utils/date.js';
import {
defaultPerPage,
@@ -37,9 +36,7 @@
import Alert from '$lib/components/Alert.svelte';
import EventTimeline from '$lib/components/EventTimeline.svelte';
import CellCopy from '$lib/components/table/CopyCell.svelte';
import Button from '$lib/components/Button.svelte';
import FormGrid from '$lib/components/FormGrid.svelte';
import FormFooter from '$lib/components/FormFooter.svelte';
import FormColumns from '$lib/components/FormColumns.svelte';
import FormColumn from '$lib/components/FormColumn.svelte';
import EventName from '$lib/components/table/EventName.svelte';
@@ -1039,12 +1036,12 @@
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1.5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75"
/>
</svg>
</StatsCard>
@@ -1073,18 +1070,17 @@
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1.5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
</StatsCard>
@@ -1164,12 +1160,12 @@
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1.5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</StatsCard>