Compare commits
2 Commits
4af12dac40
...
911b9076c5
| Author | SHA1 | Date | |
|---|---|---|---|
| 911b9076c5 | |||
| 84d7c8b2be |
30
src/lib/BSlideOverlay.svelte
Normal file
30
src/lib/BSlideOverlay.svelte
Normal file
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { slide, fade } from 'svelte/transition';
|
||||
import type { Snippet } from "svelte";
|
||||
|
||||
let { show = $bindable(), closable = true, children }: {
|
||||
show: boolean,
|
||||
closable: boolean
|
||||
children: Snippet<[{ closeOverlay: () => void }]>
|
||||
} = $props();
|
||||
|
||||
function closeOverlay() {
|
||||
if (!closable) return;
|
||||
show = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if show}
|
||||
<div class="fixed inset-0 h-screen w-screen z-20 pointer-events-auto flex flex-col justify-end items-center">
|
||||
<button type="button" class="bg-base-100/50 w-full grow z-20"
|
||||
transition:fade={{duration:200}}
|
||||
onclick={closeOverlay}
|
||||
aria-label="Close Overlay"
|
||||
></button>
|
||||
<div class="h-fit flex flex-col items-center gap-2 rounded-t-md pointer-events-auto p-2 z-20"
|
||||
data-theme="light"
|
||||
transition:slide>
|
||||
{@render children({ closeOverlay })}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
49
src/lib/ToastService/Toast.svelte
Normal file
49
src/lib/ToastService/Toast.svelte
Normal file
@@ -0,0 +1,49 @@
|
||||
<script lang="ts">
|
||||
import type { ToastType } from "$lib/ToastService/types";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
const { toast, destroyer }: {
|
||||
toast: ToastType,
|
||||
destroyer: () => void
|
||||
} = $props();
|
||||
|
||||
const alertClass = $derived.by(() => {
|
||||
switch (toast.type) {
|
||||
case "success":
|
||||
return 'alert-success';
|
||||
case "warning":
|
||||
return 'alert-warning';
|
||||
case "error":
|
||||
return 'alert-error';
|
||||
case "info":
|
||||
return 'alert-info';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
let timeMs = $state(0);
|
||||
let expireTime = 50000;
|
||||
let progressMax = 100;
|
||||
let segmentTime = Math.round(expireTime / progressMax);
|
||||
const progressValue = $derived(Math.round(timeMs / segmentTime));
|
||||
|
||||
function increment() {
|
||||
timeMs += segmentTime;
|
||||
if (timeMs >= expireTime) destroyer();
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const interval = setInterval(increment, segmentTime);
|
||||
return () => clearInterval(interval);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="w-[60vw]">
|
||||
<div class="absolute w-full flex flex-col px-2">
|
||||
<progress class="progress h-1 w-full" value={progressValue} max="100"></progress>
|
||||
</div>
|
||||
<div class={["alert", alertClass]}>
|
||||
<span>{toast.content}</span>
|
||||
</div>
|
||||
</div>
|
||||
27
src/lib/ToastService/ToastProvider.svelte
Normal file
27
src/lib/ToastService/ToastProvider.svelte
Normal file
@@ -0,0 +1,27 @@
|
||||
<script lang="ts">
|
||||
import type { ServiceState, ToastType } from "$lib/ToastService/types";
|
||||
import { setContext } from "svelte";
|
||||
import Toast from "./Toast.svelte";
|
||||
|
||||
const { children } = $props();
|
||||
|
||||
let serviceState: ServiceState = $state({ toasts: {}, nextId: 0 });
|
||||
|
||||
function addToast(toast: ToastType) {
|
||||
serviceState.toasts[serviceState.nextId++] = toast;
|
||||
}
|
||||
|
||||
setContext('toastservice', addToast);
|
||||
|
||||
function destroyToast(id: string) {
|
||||
delete serviceState.toasts[id];
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{@render children()}
|
||||
<div class="toast toast-top toast-center z-80">
|
||||
{#each Object.entries(serviceState.toasts) as [i, toast] (i)}
|
||||
<Toast {toast} destroyer={() => destroyToast(i)}/>
|
||||
{/each}
|
||||
</div>
|
||||
13
src/lib/ToastService/types.ts
Normal file
13
src/lib/ToastService/types.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export type ToastType = {
|
||||
type: 'info' | 'success' | 'warning' | 'error',
|
||||
content: string,
|
||||
}
|
||||
|
||||
export type InternalToasts = {
|
||||
[key: string]: ToastType,
|
||||
}
|
||||
|
||||
export type ServiceState = {
|
||||
toasts: InternalToasts,
|
||||
nextId: number,
|
||||
}
|
||||
8
src/lib/ToastService/useToast.ts
Normal file
8
src/lib/ToastService/useToast.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { getContext } from "svelte";
|
||||
import type { ToastType } from "$lib/ToastService/types";
|
||||
|
||||
export function useToast(): { fireToast: (toast: ToastType) => void } {
|
||||
return {
|
||||
fireToast: getContext('toastservice')
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
<script lang="ts">
|
||||
import '../app.css';
|
||||
|
||||
let { children } = $props();
|
||||
import '../app.css';
|
||||
import ToastProvider from "$lib/ToastService/ToastProvider.svelte";
|
||||
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
{@render children()}
|
||||
<ToastProvider>
|
||||
{@render children()}
|
||||
</ToastProvider>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { slide, scale, fade } from 'svelte/transition';
|
||||
import { scale } from 'svelte/transition';
|
||||
import { goto } from "$app/navigation";
|
||||
import BSlideOverlay from "$lib/BSlideOverlay.svelte";
|
||||
import { useToast } from "$lib/ToastService/useToast";
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
const { fireToast } = useToast();
|
||||
|
||||
let showOverlay = $state(false);
|
||||
let startingCharge = $state(false);
|
||||
|
||||
@@ -12,53 +16,44 @@
|
||||
startingCharge = true;
|
||||
try {
|
||||
// TODO: StartCharge fetch request
|
||||
// const response = await fetch(`/api/public/1/chargecontroller/${data.qrcode}/start_charge/`, {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Accept': 'application/json',
|
||||
// }
|
||||
// });
|
||||
console.log("nop");
|
||||
await goto(`/${data.qrcode}/status`);
|
||||
} finally {
|
||||
startingCharge = false;
|
||||
}
|
||||
}
|
||||
|
||||
function closeOverlay() {
|
||||
if (startingCharge) return;
|
||||
showOverlay = false;
|
||||
}
|
||||
|
||||
// TODO: Try to make a real component out of this "modal" using a Portal: https://stackoverflow.com/a/62733195
|
||||
</script>
|
||||
|
||||
<div class="grid grid-rows-6 h-full w-full">
|
||||
<div class="row-start-5 col-start-1 flex flex-col justify-center items-center">
|
||||
<div class="pointer-events-auto">
|
||||
<button class="btn btn-primary btn-lg uppercase" onclick={() => showOverlay = true}>Attivare la Ricarica</button>
|
||||
<button class="btn btn-secondary" onclick={() => fireToast({type: 'success', content: 'Messaggio Ricevuto!'})}>
|
||||
TEST
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{#if showOverlay}
|
||||
<button type="button" class="row-start-1 row-span-6 col-start-1 z-10 bg-base-100/50 pointer-events-auto"
|
||||
transition:fade={{duration:200}}
|
||||
onclick={closeOverlay}
|
||||
aria-label="Close Overlay"
|
||||
></button>
|
||||
{/if}
|
||||
<div class="row-start-1 row-span-6 col-start-1 flex flex-col justify-end items-center">
|
||||
{#if showOverlay}
|
||||
<div class="h-fit flex flex-col items-center gap-2 rounded-t-md pointer-events-auto p-2 z-20"
|
||||
data-theme="light"
|
||||
transition:slide>
|
||||
<!--suppress ALL -->
|
||||
<enhanced:img alt="Please attach the connector to the car" src="./attach_car.png"/>
|
||||
<div class="font-bold uppercase text-md text-gray-700">Avete connesso il cavo alla vostra auto?</div>
|
||||
<div class="flex flex-row justify-around gap-4 w-full">
|
||||
<button class="btn btn-primary btn-xl w-24 grid" onclick={startCharge}>
|
||||
{#if startingCharge}
|
||||
<span transition:scale class="col-start-1 col-end-2 row-start-1 row-end-2 loading"></span>
|
||||
{:else}
|
||||
<span transition:scale class="col-start-1 col-end-2 row-start-1 row-end-2">SI</span>
|
||||
{/if}
|
||||
</button>
|
||||
<button class="btn btn-secondary btn-xl w-24" onclick={closeOverlay}>NO</button>
|
||||
</div>
|
||||
<BSlideOverlay bind:show={showOverlay} closable={!startingCharge}>
|
||||
{#snippet children({ closeOverlay })}
|
||||
<!--suppress ALL -->
|
||||
<enhanced:img alt="Please attach the connector to the car" src="./attach_car.png"/>
|
||||
<div class="font-bold uppercase text-md text-gray-700">Avete connesso il cavo alla vostra auto?</div>
|
||||
<div class="flex flex-row justify-around gap-4 w-full">
|
||||
<button class="btn btn-primary btn-xl w-24 grid" onclick={startCharge}>
|
||||
{#if startingCharge}
|
||||
<span transition:scale class="col-start-1 col-end-2 row-start-1 row-end-2 loading"></span>
|
||||
{:else}
|
||||
<span transition:scale class="col-start-1 col-end-2 row-start-1 row-end-2">SI</span>
|
||||
{/if}
|
||||
</button>
|
||||
<button class="btn btn-secondary btn-xl w-24" onclick={closeOverlay}>NO</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
</BSlideOverlay>
|
||||
</div>
|
||||
@@ -1,7 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { invalidate } from "$app/navigation";
|
||||
import { goto, invalidate } from "$app/navigation";
|
||||
import { scale } from 'svelte/transition';
|
||||
import BatteryIcon from "$lib/icons/BatteryIcon.svelte";
|
||||
import BSlideOverlay from "$lib/BSlideOverlay.svelte";
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
@@ -23,7 +25,20 @@
|
||||
return (wh / 1000).toFixed(2);
|
||||
});
|
||||
|
||||
// TODO: Add Modal from bottom to confirm
|
||||
let showOverlay = $state(false);
|
||||
let stoppingCharge = $state(false);
|
||||
|
||||
async function stopCharge() {
|
||||
if (stoppingCharge) return;
|
||||
stoppingCharge = true;
|
||||
try {
|
||||
// TODO: StopCharge fetch request
|
||||
console.log("nop");
|
||||
await goto(`/${data.qrcode}`);
|
||||
} finally {
|
||||
stoppingCharge = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="pointer-events-auto bg-green-700/80 h-full w-full flex flex-col justify-center items-center gap-12">
|
||||
@@ -36,7 +51,7 @@
|
||||
<p class="text-sm">Potenza misurata: {powerkWh} kW</p>
|
||||
</div>
|
||||
<div class="w-[90%]">
|
||||
<button class="btn btn-warning btn-xl w-full">Interrompere la ricarica</button>
|
||||
<button class="btn btn-warning btn-xl w-full" onclick={() => showOverlay = true}>Interrompere la ricarica</button>
|
||||
</div>
|
||||
<div class="text-center w-[70%] flex flex-col gap-5 text-sm">
|
||||
<p>
|
||||
@@ -46,4 +61,19 @@
|
||||
Papa Francesco, Enciclica Laudato Si’
|
||||
</p>
|
||||
</div>
|
||||
<BSlideOverlay bind:show={showOverlay} closable={!stoppingCharge}>
|
||||
{#snippet children({ closeOverlay })}
|
||||
<p class="font-bold w-[90%] text-wrap uppercase text-center">Sei sicuro di voler interrompere la ricarica?</p>
|
||||
<div class="flex flex-row justify-around gap-4 w-full">
|
||||
<button class="btn btn-primary btn-xl w-24 grid" onclick={stopCharge}>
|
||||
{#if stoppingCharge}
|
||||
<span transition:scale class="col-start-1 col-end-2 row-start-1 row-end-2 loading"></span>
|
||||
{:else}
|
||||
<span transition:scale class="col-start-1 col-end-2 row-start-1 row-end-2">SI</span>
|
||||
{/if}
|
||||
</button>
|
||||
<button class="btn btn-secondary btn-xl w-24" onclick={closeOverlay}>NO</button>
|
||||
</div>
|
||||
{/snippet}
|
||||
</BSlideOverlay>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user