[web] Improve modals
This commit is contained in:
parent
863254f846
commit
bb722b437e
76
web/src/lib/ConfirmModal.svelte
Normal file
76
web/src/lib/ConfirmModal.svelte
Normal file
@ -0,0 +1,76 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import type bootstrap from 'bootstrap';
|
||||
import { beforeNavigate } from '$app/navigation';
|
||||
|
||||
export let modalTitle = 'Confirm';
|
||||
export let modalText = 'Are you sure?';
|
||||
|
||||
let confirmModal: HTMLDivElement;
|
||||
let modal: bootstrap.Modal | undefined;
|
||||
|
||||
let confirmAction: (() => void) | undefined;
|
||||
let cancelAction: (() => void) | undefined;
|
||||
|
||||
export function confirm() {
|
||||
if (confirmAction !== undefined) {
|
||||
confirmAction();
|
||||
}
|
||||
}
|
||||
|
||||
export function cancel() {
|
||||
if (cancelAction !== undefined) {
|
||||
cancelAction();
|
||||
}
|
||||
}
|
||||
|
||||
export async function prompt(text = 'Are you sure?', title = 'Confirm'): Promise<boolean> {
|
||||
modalText = text;
|
||||
modalTitle = title;
|
||||
if (modal === undefined) {
|
||||
return false;
|
||||
}
|
||||
modal.show();
|
||||
return new Promise((resolve) => {
|
||||
confirmAction = () => {
|
||||
resolve(true);
|
||||
modal?.hide();
|
||||
};
|
||||
|
||||
cancelAction = () => {
|
||||
resolve(false);
|
||||
modal?.hide();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
const bootstrap = await import('bootstrap');
|
||||
modal = new bootstrap.Modal(confirmModal);
|
||||
});
|
||||
|
||||
beforeNavigate(() => {
|
||||
modal?.hide();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div bind:this={confirmModal} class="modal fade" tabindex="-1" data-bs-backdrop="static">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="exampleModalLabel">{modalTitle}</h1>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{#if $$slots.default}
|
||||
<slot />
|
||||
{:else}
|
||||
{modalText}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button on:click={cancel} type="button" class="btn btn-secondary">Cancel</button>
|
||||
<button on:click={confirm} type="button" class="btn btn-primary">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
46
web/src/lib/FormAlert.svelte
Normal file
46
web/src/lib/FormAlert.svelte
Normal file
@ -0,0 +1,46 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { afterUpdate } from 'svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
|
||||
let dismissed = false;
|
||||
let success = false;
|
||||
let message: string | undefined;
|
||||
let manualPopup = false;
|
||||
|
||||
$: if ($page.form !== null) {
|
||||
manualPopup = false;
|
||||
dismissed = false;
|
||||
success = $page.form.success;
|
||||
message = $page.form.message;
|
||||
}
|
||||
|
||||
afterUpdate(() => {
|
||||
if ($page.form !== null) {
|
||||
success = $page.form.success;
|
||||
}
|
||||
});
|
||||
|
||||
export function popup(data: { success: boolean; message?: string }) {
|
||||
dismissed = false;
|
||||
manualPopup = true;
|
||||
success = data.success;
|
||||
message = data.message;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if ($page.form !== null && dismissed === false) || (manualPopup === true && dismissed === false)}
|
||||
<div
|
||||
transition:slide|local
|
||||
class={`mt-2 mb-2 alert alert-dismissible alert-${success ? 'success' : 'danger'}`}
|
||||
>
|
||||
{success ? 'Success' : message ?? 'Unknown error'}
|
||||
<button
|
||||
on:click={() => {
|
||||
dismissed = true;
|
||||
}}
|
||||
type="button"
|
||||
class="btn-close"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
@ -8,12 +8,10 @@
|
||||
<title>Contests</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1 style="text-align:center" class="mb-4"><i class="bi bi-flag"></i> Contests</h1>
|
||||
<h1 style="text-align:center" class="mb-1"><i class="bi bi-flag"></i> Contests</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="text-end">
|
||||
<div class="d-flex flex-row justify-content-end">
|
||||
<a href="/admin/contests/create" class="btn btn-outline-success">Create</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 list-group">
|
||||
|
@ -1,26 +1,29 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from '$app/forms';
|
||||
import { page } from '$app/stores';
|
||||
import type { Actions, PageData } from './$types';
|
||||
import ConfirmModal from '$lib/ConfirmModal.svelte';
|
||||
import FormAlert from '$lib/FormAlert.svelte';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
export let form: Actions;
|
||||
|
||||
let confirmModal: ConfirmModal;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{data.name}</title>
|
||||
</svelte:head>
|
||||
|
||||
<ConfirmModal bind:this={confirmModal} />
|
||||
|
||||
<h1 style="text-align:center" class="mb-4">{data.name}</h1>
|
||||
|
||||
<FormAlert />
|
||||
|
||||
{#if data.activeTeams !== 0}
|
||||
<div class="alert alert-success">In Progress</div>
|
||||
{/if}
|
||||
|
||||
{#if form && !form.success}
|
||||
<div class="alert alert-danger">An error occured</div>
|
||||
{/if}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<a href="/admin/contests" class="btn btn-outline-primary">All Contests</a>
|
||||
@ -28,12 +31,12 @@
|
||||
<div class="col-6 text-end">
|
||||
<form
|
||||
method="POST"
|
||||
use:enhance={({ cancel }) => {
|
||||
if (!confirm('Are you sure?')) {
|
||||
use:enhance={async ({ cancel }) => {
|
||||
if ((await confirmModal.prompt('Are you sure?')) !== true) {
|
||||
cancel();
|
||||
}
|
||||
return async ({ update }) => {
|
||||
update();
|
||||
await update();
|
||||
};
|
||||
}}
|
||||
>
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from '$app/forms';
|
||||
import { goto } from '$app/navigation';
|
||||
import FormAlert from '$lib/FormAlert.svelte';
|
||||
import type { Actions, PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
@ -39,14 +40,12 @@
|
||||
<title>Create Contest</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1 style="text-align:center" class="mb-4">Create Contest</h1>
|
||||
<h1 style="text-align:center" class="mb-4"><i class="bi bi-flag"></i> Create Contest</h1>
|
||||
|
||||
<FormAlert />
|
||||
|
||||
<a href="/admin/contests" class="mb-3 btn btn-outline-secondary">Cancel</a>
|
||||
|
||||
{#if form && !form.success}
|
||||
<div class="alert alert-danger">Invalid entry</div>
|
||||
{/if}
|
||||
|
||||
<form method="POST" action="?/create" use:enhance>
|
||||
<h4>Name</h4>
|
||||
<input name="name" class="form-control" />
|
||||
|
@ -6,6 +6,7 @@
|
||||
import { enhance } from '$app/forms';
|
||||
import { goto } from '$app/navigation';
|
||||
import { stretchTextarea } from '$lib/util';
|
||||
import FormAlert from '$lib/FormAlert.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
export let form: Actions;
|
||||
@ -58,11 +59,7 @@
|
||||
|
||||
<h1 style="text-align:center" class="mb-4">Review Submission</h1>
|
||||
|
||||
{#if form && !form.success}
|
||||
<div class="alert alert-danger">Submission was not successful</div>
|
||||
{:else if form && form.success}
|
||||
<div class="alert alert-success">Success!</div>
|
||||
{/if}
|
||||
<FormAlert />
|
||||
|
||||
<div class="mb-3 col">
|
||||
<a href="/admin/reviews" class="btn btn-outline-primary">All Reviews</a>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<title>Problems</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1 style="text-align:center" class="mb-4"><i class="bi bi-question-circle"></i> Problems</h1>
|
||||
<h1 style="text-align:center" class="mb-1"><i class="bi bi-question-circle"></i> Problems</h1>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="text-end">
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import ConfirmModal from '$lib/ConfirmModal.svelte';
|
||||
import { stretchTextarea } from '$lib/util';
|
||||
import type { Actions, PageData } from './$types';
|
||||
|
||||
@ -11,7 +12,7 @@
|
||||
export let form: Actions;
|
||||
|
||||
async function deleteProblem() {
|
||||
const sure = confirm('Are you sure?');
|
||||
const sure = await confirmModal.prompt('Are you sure?');
|
||||
if (!sure) {
|
||||
return;
|
||||
}
|
||||
@ -23,9 +24,15 @@
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
let confirmModal: ConfirmModal;
|
||||
</script>
|
||||
|
||||
<h1 style="text-align:center" class="mb-4">{data.problemData.friendlyName}</h1>
|
||||
<ConfirmModal bind:this={confirmModal} />
|
||||
|
||||
<h1 style="text-align:center" class="mb-1">
|
||||
<i class="bi bi-question-circle"></i> Problem - {data.problemData.friendlyName}
|
||||
</h1>
|
||||
|
||||
{#if error}
|
||||
<div class="alert alert-danger">
|
||||
|
@ -27,7 +27,7 @@
|
||||
<title>Reviews</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1 style="text-align:center" class="mb-4"><i class="bi bi-eye"></i> Reviews</h1>
|
||||
<h1 style="text-align:center" class="mb-1"><i class="bi bi-eye"></i> Reviews</h1>
|
||||
|
||||
<div class="mb-3 text-end">
|
||||
{#if updating}
|
||||
|
@ -25,7 +25,7 @@
|
||||
<title>Admin Scoreboard</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1 style="text-align:center" class="mb-4"><i class="bi bi-trophy"></i> Admin Scoreboards</h1>
|
||||
<h1 style="text-align:center" class="mb-1"><i class="bi bi-trophy"></i> Admin Scoreboards</h1>
|
||||
|
||||
<div class="text-end">
|
||||
{#if updating}
|
||||
|
@ -5,6 +5,7 @@
|
||||
import 'diff2html/bundles/css/diff2html.min.css';
|
||||
import { enhance } from '$app/forms';
|
||||
import { stretchTextarea } from '$lib/util';
|
||||
import ConfirmModal from '$lib/ConfirmModal.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
export let form: Actions;
|
||||
@ -25,12 +26,16 @@
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let confirmModal: ConfirmModal;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Submission</title>
|
||||
</svelte:head>
|
||||
|
||||
<ConfirmModal bind:this={confirmModal} />
|
||||
|
||||
<h1 style="text-align:center" class="mb-4">Submission</h1>
|
||||
|
||||
{#if form && !form.success}
|
||||
@ -45,12 +50,12 @@
|
||||
<form
|
||||
method="POST"
|
||||
action="?/delete"
|
||||
use:enhance={({ cancel }) => {
|
||||
if (!confirm('Are you sure?')) {
|
||||
use:enhance={async ({ cancel }) => {
|
||||
if ((await confirmModal.prompt('Are you sure?')) !== true) {
|
||||
cancel();
|
||||
}
|
||||
return async ({ update }) => {
|
||||
update();
|
||||
await update();
|
||||
};
|
||||
}}
|
||||
>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<title>Teams</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1 style="text-align:center" class="mb-4"><i class="bi bi-people"></i> Teams</h1>
|
||||
<h1 style="text-align:center" class="mb-1"><i class="bi bi-people"></i> Teams</h1>
|
||||
|
||||
{#if form && !form.success}
|
||||
<div class="alert alert-danger">Invalid action</div>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from '$app/forms';
|
||||
import ConfirmModal from '$lib/ConfirmModal.svelte';
|
||||
import { genPassword } from '../util';
|
||||
import type { Actions, PageData } from './$types';
|
||||
|
||||
@ -16,12 +17,16 @@
|
||||
const passEntry = document.getElementById('pass_entry') as HTMLInputElement;
|
||||
passEntry.value = genPassword();
|
||||
}
|
||||
|
||||
let confirmModal: ConfirmModal;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Team</title>
|
||||
</svelte:head>
|
||||
|
||||
<ConfirmModal bind:this={confirmModal} />
|
||||
|
||||
<h1 style="text-align:center" class="mb-4">{data.team.name}</h1>
|
||||
|
||||
<div class="row">
|
||||
@ -32,12 +37,12 @@
|
||||
<form
|
||||
method="POST"
|
||||
action="?/delete"
|
||||
use:enhance={({ cancel }) => {
|
||||
if (!confirm('Are you sure?')) {
|
||||
use:enhance={async ({ cancel }) => {
|
||||
if ((await confirmModal.prompt('Are you sure?')) !== true) {
|
||||
cancel();
|
||||
}
|
||||
return async ({ update }) => {
|
||||
update();
|
||||
await update();
|
||||
};
|
||||
}}
|
||||
>
|
||||
|
@ -7,6 +7,9 @@ const config = {
|
||||
|
||||
kit: {
|
||||
adapter: adapter()
|
||||
},
|
||||
vitePlugin: {
|
||||
inspector: true
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user