Update theme, bands, modes and header style

This commit is contained in:
Jakob Kordež 2023-11-12 18:40:18 +01:00
parent 925e63121d
commit 3c2708b1dc
7 changed files with 229 additions and 56 deletions

View File

@ -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`;
}

View File

@ -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>
);
}

View File

@ -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>

View File

@ -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];

View File

@ -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];

View File

@ -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 {

View File

@ -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',
},
},
],
},
};