mirror of
https://github.com/jakobkordez/ham-reserve.git
synced 2025-05-31 08:49:06 +00:00
Update theme, bands, modes and header style
This commit is contained in:
parent
925e63121d
commit
3c2708b1dc
@ -4,7 +4,7 @@ import { Event } from '@/interfaces/event.interface';
|
||||
import { Reservation } from '@/interfaces/reservation.interface';
|
||||
import { dayInMs, hourInMs } from '@/util/date.util';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Band } from '@/enums/band.enum';
|
||||
import { Band, COMMON_BANDS } from '@/enums/band.enum';
|
||||
import { Mode } from '@/enums/mode.enum';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import {
|
||||
@ -18,9 +18,12 @@ export function FreeDatesComponent({ event }: { event: Event }) {
|
||||
const [date, setDate] = useState<string>(
|
||||
new Date().toISOString().slice(0, 10),
|
||||
);
|
||||
const [band, setBand] = useState<Band>();
|
||||
const [mode, setMode] = useState<Mode>();
|
||||
|
||||
const bands = Object.values(Band).map((val) => val.toString());
|
||||
const bands = band
|
||||
? [band.toString()]
|
||||
: COMMON_BANDS.map((band) => band.toString());
|
||||
|
||||
useEffect(() => {
|
||||
setReservations(undefined);
|
||||
@ -113,6 +116,30 @@ export function FreeDatesComponent({ event }: { event: Event }) {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-control">
|
||||
<label className="label">
|
||||
<span className="label-text">Frekvenčni pas</span>
|
||||
</label>
|
||||
<select
|
||||
className="select select-bordered w-full"
|
||||
value={band?.toString() ?? ''}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
if (val == '') setBand(undefined);
|
||||
else setBand(val as Band);
|
||||
}}
|
||||
>
|
||||
<option value="">---</option>
|
||||
{Object.values(Band).map((band) => (
|
||||
<option key={band}>{band}</option>
|
||||
))}
|
||||
</select>
|
||||
<label className="label">
|
||||
<span className="label-text-alt">
|
||||
Tukaj se nahajajo vsi frekvenčni pasovi
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-control">
|
||||
<label className="label">
|
||||
<span className="label-text">Način</span>
|
||||
@ -127,31 +154,31 @@ export function FreeDatesComponent({ event }: { event: Event }) {
|
||||
}}
|
||||
>
|
||||
<option value="">Vsi</option>
|
||||
{Object.values(Mode).map((band) => (
|
||||
<option key={band}>{band}</option>
|
||||
{Object.values(Mode).map((mode) => (
|
||||
<option key={mode}>{mode}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-[2] overflow-x-auto">
|
||||
<div className="flex-[2]">
|
||||
<table className="table mx-auto w-auto">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td />
|
||||
<td>
|
||||
<div className="flex">
|
||||
<div className="w-12 border-l border-base-300 pl-0.5">0</div>
|
||||
<div className="w-12 border-l border-base-300 pl-0.5">4</div>
|
||||
<div className="w-12 border-l border-base-300 pl-0.5">8</div>
|
||||
<div className="w-12 border-l border-base-300 pl-0.5">12</div>
|
||||
<div className="w-12 border-l border-base-300 pl-0.5">16</div>
|
||||
<div className="w-12 border-l border-base-300 pl-0.5">20</div>
|
||||
<div className="w-12 border-l border-base-300 pl-1">0</div>
|
||||
<div className="w-12 border-l border-base-300 pl-1">4</div>
|
||||
<div className="w-12 border-l border-base-300 pl-1">8</div>
|
||||
<div className="w-12 border-l border-base-300 pl-1">12</div>
|
||||
<div className="w-12 border-l border-base-300 pl-1">16</div>
|
||||
<div className="w-12 border-l border-base-300 pl-1">20</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{Object.values(Band).map((band, bi) => (
|
||||
{bands.map((band, bi) => (
|
||||
<tr key={band}>
|
||||
<th key={band} className="px-3 py-2">
|
||||
{band}
|
||||
@ -161,18 +188,16 @@ export function FreeDatesComponent({ event }: { event: Event }) {
|
||||
{freeTable[bi].map((taken, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={`m-auto h-3 w-3 border-l border-base-200 first:rounded-l-full first:border-0 last:rounded-r-full ${
|
||||
taken?.size ?? 0 ? 'tooltip' : ''
|
||||
} ${
|
||||
className={`tooltip m-auto h-3 w-3 border-l border-base-200 first:rounded-l-full first:border-0 last:rounded-r-full ${
|
||||
taken == null
|
||||
? 'bg-base-200'
|
||||
: taken.size == 0
|
||||
? 'bg-green-500/90'
|
||||
: 'bg-red-500/90'
|
||||
: mode || taken.size == Object.values(Mode).length
|
||||
? 'bg-red-500/90'
|
||||
: 'bg-yellow-500/90'
|
||||
}`}
|
||||
data-tip={`Zasedeni načini: ${Array.from(
|
||||
taken?.values() ?? [],
|
||||
).join(', ')}`}
|
||||
data-tip={`${formatTime(i)} - ${formatTime(i + 1)} UTC`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -185,3 +210,7 @@ export function FreeDatesComponent({ event }: { event: Event }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function formatTime(hour: number) {
|
||||
return `${hour.toString().padStart(2, '0')}:00`;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { apiFunctions } from '@/api';
|
||||
import { Band } from '@/enums/band.enum';
|
||||
import { Mode } from '@/enums/mode.enum';
|
||||
import { Band, COMMON_BANDS } from '@/enums/band.enum';
|
||||
import { COMMON_MODES, Mode } from '@/enums/mode.enum';
|
||||
import { Event } from '@/interfaces/event.interface';
|
||||
import { User } from '@/interfaces/user.interface';
|
||||
import { useUserState } from '@/state/user-state';
|
||||
@ -23,16 +23,43 @@ function padZero(num: number) {
|
||||
return num.toString().padStart(2, '0');
|
||||
}
|
||||
|
||||
function formatHours(start: Date, end: Date) {
|
||||
const hours = (end.valueOf() - start.valueOf()) / hourInMs;
|
||||
switch (hours) {
|
||||
case 1:
|
||||
return '1 ura';
|
||||
case 2:
|
||||
return '2 uri';
|
||||
case 3:
|
||||
case 4:
|
||||
return hours + ' ure';
|
||||
default:
|
||||
return hours + ' ur';
|
||||
}
|
||||
}
|
||||
|
||||
export function ReserveComponent({ event }: ReserveComponentProps) {
|
||||
const [user, setUser] = useState<User | null>();
|
||||
const getUser = useUserState((state) => state.getUser);
|
||||
|
||||
const [availableBands, setAvailableBands] = useState<Set<Band>>(
|
||||
new Set(COMMON_BANDS),
|
||||
);
|
||||
const [availableModes, setAvailableModes] = useState<Set<Mode>>(
|
||||
new Set(COMMON_MODES),
|
||||
);
|
||||
|
||||
const [startDT, setStartDT] = useState<Date>(floorHour(Date.now()));
|
||||
const [endDT, setEndDT] = useState<Date>(floorHour(Date.now() + hourInMs));
|
||||
const [bands, setBands] = useState<Set<Band>>(new Set());
|
||||
const [modes, setModes] = useState<Set<Mode>>(new Set());
|
||||
const [error, setError] = useState<string>();
|
||||
|
||||
const allValid =
|
||||
startDT.valueOf() < endDT.valueOf() && bands.size > 0 && modes.size > 0;
|
||||
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
getUser().then(setUser);
|
||||
}, [getUser]);
|
||||
@ -59,11 +86,10 @@ export function ReserveComponent({ event }: ReserveComponentProps) {
|
||||
const msg = err.response.data.message;
|
||||
if (msg instanceof Array) setError(msg.join(', '));
|
||||
else setError(msg);
|
||||
setModalOpen(false);
|
||||
});
|
||||
}
|
||||
|
||||
// const dates = getNextNDays(7, event);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6 rounded-lg border p-6">
|
||||
{/* <div className="tabs-boxed tabs">
|
||||
@ -113,29 +139,12 @@ export function ReserveComponent({ event }: ReserveComponentProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* <div className="mt-3 flex flex-wrap gap-1">
|
||||
{dates.map((dt, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => {
|
||||
setDate(dt);
|
||||
dateRef.current!.value = dt.toISOString().slice(0, 10);
|
||||
}}
|
||||
className={`btn btn-sm flex-1 ${
|
||||
date?.valueOf() === dt?.valueOf() ? 'btn-primary' : ''
|
||||
}`}
|
||||
>
|
||||
{getUTCDMString(dt)}
|
||||
</button>
|
||||
))}
|
||||
</div> */}
|
||||
|
||||
<div className="form-control">
|
||||
<label className="label">
|
||||
<span className="label-text font-bold">Frekvenčna področja</span>
|
||||
</label>
|
||||
<div className="grid grid-cols-3 gap-3 sm:grid-cols-4">
|
||||
{Object.values(Band).map((band) => {
|
||||
{Array.from(availableBands).map((band) => {
|
||||
const checked = bands.has(band);
|
||||
function toggle() {
|
||||
if (!checked) setBands(new Set(bands).add(band));
|
||||
@ -157,18 +166,35 @@ export function ReserveComponent({ event }: ReserveComponentProps) {
|
||||
checked={checked}
|
||||
onChange={() => toggle()}
|
||||
aria-label={band}
|
||||
className="btn btn-outline btn-sm"
|
||||
className="btn btn-sm"
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
<select
|
||||
className="select select-sm bg-base-200 text-center font-bold uppercase md:px-3"
|
||||
value=""
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
if (!val) return;
|
||||
setBands(new Set(bands).add(val as Band));
|
||||
setAvailableBands(new Set(availableBands).add(val as Band));
|
||||
}}
|
||||
>
|
||||
<option value="">Ostala</option>
|
||||
{Object.values(Band).map((band) => (
|
||||
<option key={band}>{band}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-control">
|
||||
<label className="label">
|
||||
<span className="label-text font-bold">Načini</span>
|
||||
</label>
|
||||
<div className="grid grid-cols-3 gap-3 sm:grid-cols-4">
|
||||
{Object.values(Mode).map((mode) => {
|
||||
{Array.from(availableModes).map((mode) => {
|
||||
const checked = modes.has(mode);
|
||||
function toggle() {
|
||||
if (!checked) setModes(new Set(modes).add(mode));
|
||||
@ -190,10 +216,26 @@ export function ReserveComponent({ event }: ReserveComponentProps) {
|
||||
checked={checked}
|
||||
onChange={() => toggle()}
|
||||
aria-label={mode}
|
||||
className="btn btn-outline btn-sm"
|
||||
className="btn btn-sm"
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
<select
|
||||
className="select select-sm bg-base-200 text-center font-bold uppercase md:px-3"
|
||||
value=""
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
if (!val) return;
|
||||
setModes(new Set(modes).add(val as Mode));
|
||||
setAvailableModes(new Set(availableModes).add(val as Mode));
|
||||
}}
|
||||
>
|
||||
<option value="">Ostali</option>
|
||||
{Object.values(Mode).map((mode) => (
|
||||
<option key={mode}>{mode}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -204,9 +246,64 @@ export function ReserveComponent({ event }: ReserveComponentProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button className="btn btn-primary" onClick={submit}>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={() => setModalOpen(true)}
|
||||
disabled={!allValid}
|
||||
>
|
||||
Rezerviraj
|
||||
</button>
|
||||
|
||||
<div className={`modal ${modalOpen ? 'modal-open' : ''}`}>
|
||||
<div className="modal-box">
|
||||
<h3 className="text-lg font-bold">Potrdi rezervacijo</h3>
|
||||
<p className="py-4">
|
||||
Previdno preglej podatke in potrdi rezervacijo.
|
||||
</p>
|
||||
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Klicni znak:</th>
|
||||
<td>{event.callsign}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Začetek:</th>
|
||||
<td>{getUTCString(startDT)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Konec:</th>
|
||||
<td>{getUTCString(endDT)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Čas:</th>
|
||||
<td>{formatHours(startDT, endDT)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Frekvenčna področja:</th>
|
||||
<td>{Array.from(bands).join(', ')}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Načini:</th>
|
||||
<td>{Array.from(modes).join(', ')}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div className="modal-action">
|
||||
<form method="dialog">
|
||||
<button className="btn" onClick={() => setModalOpen(false)}>
|
||||
Prekliči
|
||||
</button>
|
||||
</form>
|
||||
<form method="dialog">
|
||||
<button className="btn btn-primary" onClick={submit}>
|
||||
Potrdi
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import { useEffect, useState } from 'react';
|
||||
|
||||
export function Header() {
|
||||
return (
|
||||
<div className="flex h-16 select-none justify-between bg-primary text-primary-content shadow-md dark:border-b dark:border-primary">
|
||||
<div className="flex h-16 select-none justify-between bg-primary text-primary-content shadow-md dark:border-b dark:border-primary dark:bg-base-200 dark:text-base-content dark:shadow-lg dark:shadow-primary/20">
|
||||
<Link href="/" className="my-auto ml-4 flex-1 text-2xl font-semibold">
|
||||
Ham Reserve
|
||||
</Link>
|
||||
|
@ -1,6 +1,8 @@
|
||||
export enum Band {
|
||||
b2200m = '2200m',
|
||||
b160m = '160m',
|
||||
b80m = '80m',
|
||||
b60m = '60m',
|
||||
b40m = '40m',
|
||||
b30m = '30m',
|
||||
b20m = '20m',
|
||||
@ -11,17 +13,28 @@ export enum Band {
|
||||
b6m = '6m',
|
||||
b4m = '4m',
|
||||
b2m = '2m',
|
||||
b70cm = '70cm',
|
||||
b24cm = '24cm',
|
||||
b13cm = '13cm',
|
||||
b9cm = '9cm',
|
||||
b6cm = '6cm',
|
||||
b3cm = '3cm',
|
||||
b12mm = '12mm',
|
||||
b6mm = '6mm',
|
||||
b4mm = '4mm',
|
||||
b2_5mm = '2.5mm',
|
||||
b2mm = '2mm',
|
||||
b1mm = '1mm',
|
||||
}
|
||||
|
||||
export const hfBands = [
|
||||
export const COMMON_BANDS = [
|
||||
Band.b160m,
|
||||
Band.b80m,
|
||||
Band.b40m,
|
||||
Band.b20m,
|
||||
Band.b15m,
|
||||
Band.b10m,
|
||||
Band.b6m,
|
||||
Band.b2m,
|
||||
Band.b70cm,
|
||||
];
|
||||
|
||||
export const warcBands = [Band.b30m, Band.b17m, Band.b12m];
|
||||
|
||||
export const vhfBands = [Band.b6m, Band.b4m, Band.b2m];
|
||||
|
@ -2,5 +2,12 @@ export enum Mode {
|
||||
CW = 'CW',
|
||||
SSB = 'SSB',
|
||||
FM = 'FM',
|
||||
DIGI = 'DIGI',
|
||||
AM = 'AM',
|
||||
FT8 = 'FT8',
|
||||
FT4 = 'FT4',
|
||||
RTTY = 'RTTY',
|
||||
PSK = 'PSK',
|
||||
SSTV = 'SSTV',
|
||||
}
|
||||
|
||||
export const COMMON_MODES = [Mode.CW, Mode.SSB, Mode.FM, Mode.FT8];
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
export const THEME_DARK = 'night';
|
||||
export const THEME_DARK = 'forest';
|
||||
export const THEME_LIGHT = 'lemonade';
|
||||
|
||||
interface ThemeState {
|
||||
|
@ -2,7 +2,7 @@ import type { Config } from 'tailwindcss';
|
||||
|
||||
const config: Config = {
|
||||
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
|
||||
darkMode: ['class', '[data-theme="night"]'],
|
||||
darkMode: ['class', '[data-theme="forest"]'],
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
@ -19,8 +19,35 @@ const config: Config = {
|
||||
plugins: [require('daisyui')],
|
||||
daisyui: {
|
||||
logs: false,
|
||||
darkTheme: 'night',
|
||||
themes: ['lemonade', 'night'],
|
||||
darkTheme: 'forest',
|
||||
themes: [
|
||||
{
|
||||
lemonade: {
|
||||
primary: '#529b03',
|
||||
secondary: '#e9e92f',
|
||||
accent: '#f6f9c8',
|
||||
neutral: '#191a3e',
|
||||
'base-100': '#ffffff',
|
||||
info: '#3abff8',
|
||||
success: '#36d399',
|
||||
warning: '#fbbd23',
|
||||
error: '#f87272',
|
||||
},
|
||||
},
|
||||
{
|
||||
forest: {
|
||||
primary: '#1eb854',
|
||||
secondary: '#1db990',
|
||||
accent: '#1db9ac',
|
||||
neutral: '#18342b',
|
||||
'base-100': '#202124',
|
||||
info: '#3abff8',
|
||||
success: '#36d399',
|
||||
warning: '#fbbd23',
|
||||
error: '#f87272',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user