diff --git a/frontend/src/components/Confirm.tsx b/frontend/src/components/Confirm.tsx index f732e46..b209ecb 100644 --- a/frontend/src/components/Confirm.tsx +++ b/frontend/src/components/Confirm.tsx @@ -1,6 +1,5 @@ -import { createContext, useCallback, useContext, useMemo, useRef, useState, type ReactNode } from 'react' +import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, type ReactNode } from 'react' import { Icon } from './Icon' -import { Modal } from './Modal' import { Button } from './ui' /** @@ -76,41 +75,95 @@ export function ConfirmProvider({ children }: { children: ReactNode }) { {children} - {pending && ( - settle(false)} - footer={ -
- - -
- } - > -
- {pending.destructive && ( -
- -

- Tej operacji nie da się cofnąć. -

+ {pending && } + + ) +} + +/** + * Samo okno pytania. + * + * NIE UZYWA `Modal`, I TO JEST SWIADOME + * ------------------------------------------------------------------------ + * `Modal` jest zbudowany pod FORMULARZE: ma naglowek oddzielony kreska, + * przycisk zamkniecia w rogu, przewijana tresc i stopke oddzielona druga + * kreska. Przy imporcie kontaktow albo edycji banera to sluszne, ale przy + * pytaniu na dwa zdania te same elementy rozdymaja okno: dwie linie + * podzialu i krzyzyk zajmuja wtedy wiecej miejsca niz sama tresc. + * + * Okno potwierdzenia jest krotkie z natury, wiec dostaje uklad ciagly, bez + * kresek, a wyjscie prowadzi przez przycisk "Anuluj". Krzyzyk bylby trzecim + * sposobem powiedzenia "nie", obok przycisku, Escape i klikniecia w tlo. + * + * Naklatka, Escape i blokada przewijania tla sa te same co w `Modal`: + * powtarzamy zachowanie, a nie uklad. + */ +function ConfirmDialog({ + options, + onSettle, +}: { + options: ConfirmOptions + onSettle: (value: boolean) => void +}) { + useEffect(() => { + function onKey(event: KeyboardEvent) { + if (event.key === 'Escape') onSettle(false) + } + document.addEventListener('keydown', onKey) + + const previous = document.body.style.overflow + document.body.style.overflow = 'hidden' + + return () => { + document.removeEventListener('keydown', onKey) + document.body.style.overflow = previous + } + }, [onSettle]) + + return ( +
{ + if (event.target === event.currentTarget) onSettle(false) + }} + > +
+
+ {options.destructive && ( + + + + )} +
+

+ {options.title} +

+ {options.message && ( +
+ {options.message}
)} - {pending.message && ( -
{pending.message}
- )}
- - )} - +
+ +
+ + +
+
+
) } diff --git a/frontend/src/pages/orders/OrderListPage.tsx b/frontend/src/pages/orders/OrderListPage.tsx index dcf8d9c..4ef1067 100644 --- a/frontend/src/pages/orders/OrderListPage.tsx +++ b/frontend/src/pages/orders/OrderListPage.tsx @@ -320,7 +320,8 @@ function OrderDetailModal({ orderId, onClose }: { orderId: number; onClose: () = message: 'Kursant nie dostanie dostępu do kursu. Zamówienie zostaje w dzienniku jako anulowane.', confirmLabel: 'Anuluj zamówienie', - destructive: true, + // bez `destructive`: zamowienie jest dokumentem finansowym + // i zostaje w dzienniku. To zmiana stanu, nie zniszczenie. }) if (ok) cancel.mutate() }} diff --git a/frontend/src/pages/settings/BrandingSection.tsx b/frontend/src/pages/settings/BrandingSection.tsx index c3982c5..4478a2e 100644 --- a/frontend/src/pages/settings/BrandingSection.tsx +++ b/frontend/src/pages/settings/BrandingSection.tsx @@ -152,7 +152,8 @@ export function BrandingSection() { title: 'Przywrócić grafikę domyślną?', message: 'Wgrany plik zostanie usunięty, a instancja wróci do wyglądu sprzed jego dodania.', confirmLabel: 'Przywróć domyślną', - destructive: true, + // bez `destructive`: grafike da sie wgrac z powrotem, + // a "przywroc domyslna" to cofniecie, nie zniszczenie }) if (ok) remove.mutate(kind) }} diff --git a/frontend/src/pages/webinars/SpeakQueue.tsx b/frontend/src/pages/webinars/SpeakQueue.tsx index 15f52e7..d899d26 100644 --- a/frontend/src/pages/webinars/SpeakQueue.tsx +++ b/frontend/src/pages/webinars/SpeakQueue.tsx @@ -102,17 +102,17 @@ export function SpeakQueue({ if (recordingBusy) return if (egressId) { + /* + * Bez `destructive`: plik zostaje i nic nie przepada. Nieodwracalne jest + * wylacznie to, ze nagranej chwili nie da sie powtorzyc, a to za malo na + * czerwony alarm. Ostrzezenie uzyte wszedzie traci znaczenie tam, gdzie + * strata jest prawdziwa. + */ const ok = await confirm({ title: 'Zatrzymać nagrywanie?', - message: ( - <> - Nagrywanie tego spotkania zostanie zakończone, a plik trafi do zakładki Pliki.{' '} - Tego fragmentu nie nagrasz ponownie: ponowne włączenie zacznie nowe - nagranie od bieżącej chwili. - - ), + message: + 'Plik trafi do zakładki Pliki. Ponowne włączenie zacznie nowe nagranie od bieżącej chwili.', confirmLabel: 'Zatrzymaj nagrywanie', - destructive: true, }) if (!ok) return }