Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ COPY app/client/package*.json ./
RUN npm install
COPY app/client/ ./
ENV PUBLIC_API_BASE_URL=/
ENV PUBLIC_DEMO_MODE=false
ENV NODE_ENV=production
RUN npm run build

Expand Down
1 change: 1 addition & 0 deletions app/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"chart.js": "^4.5.0",
"dayjs": "^1.11.13",
"dot-env": "^0.0.1",
"svelte-loading-spinners": "^0.3.6",
"svelte5-chartjs": "^1.0.0"
}
}
23 changes: 13 additions & 10 deletions app/client/src/components/auth/PinInput.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';

let pin = Array(6).fill('');
let inputs: HTMLInputElement[] = [];

const dispatch = createEventDispatcher();
const { complete } = $props();

function handleInput(event: Event, index: number) {
const input = event.target as HTMLInputElement;
Expand All @@ -21,7 +19,7 @@
}

if (pin.every((digit) => digit !== '')) {
dispatch('complete', pin.join(''));
complete(pin.join(''));
}
}

Expand All @@ -34,18 +32,23 @@

<div class="flex justify-center space-x-2">
{#each pin as _, i}
<input
<input
bind:this={inputs[i]}
type="tel"
inputmode="numeric"
pattern="[0-9]*"
maxlength="1"
class="h-10 w-10 rounded-md border-2 border-gray-300 bg-white text-center text-xl text-gray-900 focus:border-blue-500 focus:ring-blue-500 sm:h-12 sm:w-12 sm:text-2xl
dark:border-gray-600 dark:bg-gray-900 dark:text-gray-100 dark:focus:border-blue-400 dark:focus:ring-blue-400"
style="-webkit-text-security: disc;"
on:input={(e) => handleInput(e, i)}
on:keydown={(e) => handleKeydown(e, i)}
oninput={(e) => handleInput(e, i)}
onkeydown={(e) => handleKeydown(e, i)}
aria-label={`PIN digit ${i + 1}`}
/>
{/each}
</div>

<style>
@reference "../../styles/app.css";
input {
@apply h-10 w-10 rounded-md border-2 border-gray-300 bg-white text-center text-xl text-gray-900 focus:border-blue-500 focus:ring-blue-500 sm:h-12 sm:w-12 sm:text-2xl dark:border-gray-600 dark:bg-gray-900 dark:text-gray-100 dark:focus:border-blue-400 dark:focus:ring-blue-400;
-webkit-text-security: disc;
}
</style>
5 changes: 1 addition & 4 deletions app/client/src/components/chart/ChartCard.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
<script lang="ts">
export let title: string;
export let chartData: any;
export let ChartComponent: any;
export let options: any = {};
const { title, chartData, options, ChartComponent } = $props();
</script>

<div
Expand Down
36 changes: 23 additions & 13 deletions app/client/src/components/common/FormField.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
<script lang="ts">
export let id = '';
export let type: string = 'text';
export let placeholder: string = '';
export let value: any;
export let icon: any = null;
export let required: boolean = false;
export let ariaLabel: string = '';
export let disabled: boolean = false;
export let inputClass: string = '';
export let onInput: ((e: Event) => void) | undefined = undefined;
let {
id,
type = 'text',
placeholder = '',
value = $bindable(),
icon = null,
required = false,
ariaLabel = '',
disabled = false,
inputClass = '',
onInput = undefined
} = $props();
const Icon = icon;
</script>

<div class="mb-6">
Expand All @@ -22,14 +25,21 @@
{required}
aria-label={ariaLabel}
{disabled}
on:input={onInput}
oninput={onInput}
autocomplete="off"
step=".01"
/>
{#if icon}
<svelte:component
this={icon}
<Icon
class="absolute top-1/2 left-4 h-6 w-6 -translate-y-1/2 text-gray-400 dark:text-gray-500"
aria-hidden="true"
/>
{/if}
</div>
</div>

<style>
::-webkit-calendar-picker-indicator {
filter: contrast(0.2);
}
</style>
20 changes: 20 additions & 0 deletions app/client/src/components/common/FormSubmitButton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script lang="ts">
import { Jumper } from 'svelte-loading-spinners';

const { text, loading } = $props();
</script>

<div class="flex justify-center">
{#if !loading}
<button
type="submit"
class="flex cursor-pointer gap-2 rounded-lg bg-blue-600 px-4 py-2 text-lg font-semibold text-white shadow-md dark:text-blue-200"
disabled={loading}
aria-busy={loading}
>
<span>{text}</span>
</button>
{:else}
<Jumper size="40" color="#155dfc" unit="px" duration="2s" />
{/if}
</div>
39 changes: 39 additions & 0 deletions app/client/src/components/common/ModalContainer.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script lang="ts">
import { scale } from 'svelte/transition';

const { onclose, children, title, loading } = $props();
</script>

<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
<div
in:scale={{ duration: 500 }}
class="relative max-h-[90vh] w-full max-w-xl overflow-y-auto rounded-2xl bg-white p-10 shadow-2xl dark:bg-gray-800"
>
<button
type="button"
class="absolute top-6 right-6 rounded-full bg-gray-100 p-2 text-gray-400 transition hover:text-gray-700 dark:bg-gray-700 dark:text-gray-300 dark:hover:text-white"
onclick={() => onclose()}
aria-label="Close"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
<h2 class="mb-2 text-left text-3xl font-semibold text-gray-900 dark:text-gray-100">
{title}
</h2>
<div class="mb-8 border-b border-gray-200 dark:border-gray-700"></div>
{@render children()}
</div>
</div>
14 changes: 14 additions & 0 deletions app/client/src/components/common/TabContainer.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script lang="ts">
const { title, children } = $props();
</script>

<div
class="rounded-lg bg-gray-50 p-4 dark:bg-gray-800"
role="tabpanel"
aria-labelledby="dashboard-tab"
>
<h2 class="mb-6 text-2xl font-bold text-gray-900 dark:text-gray-100">
{title}
</h2>
{@render children()}
</div>
19 changes: 10 additions & 9 deletions app/client/src/components/common/ThemeToggle.svelte
Original file line number Diff line number Diff line change
@@ -1,46 +1,47 @@
<script lang="ts">
import { browser } from '$app/environment';
import { darkModeStore } from '$lib/stores/dark-mode';
import { Moon, Sun } from '@lucide/svelte';
let darkMode = $state(false);

if (browser) {
const storedDark = localStorage.getItem('darkMode');
darkMode = storedDark === 'true';
document.documentElement.classList.toggle('dark', storedDark === 'true');
darkModeStore.set(storedDark === 'true');
}

function toggleDarkMode() {
darkMode = !darkMode;
document.documentElement.classList.toggle('dark', darkMode);
if (browser) {
localStorage.setItem('darkMode', darkMode ? 'true' : 'false');
}
darkModeStore.set(darkMode);
}
</script>

<button
type="button"
class="ml-auto flex items-center gap-2 rounded-full bg-gray-200 p-2 shadow transition-colors dark:bg-gray-700"
class="ml-auto flex items-center gap-2 rounded-full bg-gray-200 p-0.5 shadow transition-colors focus:bg-blue-600 dark:bg-gray-700"
onclick={toggleDarkMode}
aria-label="Toggle dark mode"
>
<span class="sr-only">Toggle dark mode</span>
<div class="relative flex items-center">
<!-- Switch background -->
<span
class="inline-block h-6 w-12 rounded-full transition-colors duration-300"
class:!bg-blue-500={darkMode}
class:!bg-gray-400={!darkMode}
style="background-color: {darkMode ? '#facc15' : '#d1d5db'}"
class="inline-block h-5 w-10 rounded-full bg-gray-400 transition-colors duration-300 dark:bg-gray-800"
></span>
<!-- Switch knob -->
<span
class="absolute top-0 left-0 flex h-6 w-6 items-center justify-center rounded-full bg-white shadow transition-transform duration-300"
style="transform: translateX({darkMode ? '24px' : '0px'});"
class="absolute top-0 left-0 flex h-5 w-5 items-center justify-center rounded-full bg-white shadow transition-transform duration-300 dark:bg-black"
style="transform: translateX({darkMode ? '20px' : '0px'});"
>
{#if darkMode}
<Moon class="h-4 w-4 text-yellow-600" />
<Moon class="h-3 w-3 text-white" />
{:else}
<Sun class="h-4 w-4 text-yellow-500" />
<Sun class="h-3 w-3 text-black" />
{/if}
</span>
</div>
Expand Down
Loading