This commit is contained in:
Jakob Kordež 2023-11-02 15:07:11 +01:00
parent fb54ba537f
commit 21c63f0333
18 changed files with 159 additions and 103 deletions

View File

@ -21,15 +21,17 @@ export class EventsService {
} }
findCurrent(): Promise<Event[]> { findCurrent(): Promise<Event[]> {
const now = new Date(); const now = Date.now();
const from = new Date(now - 1000 * 60 * 60 * 24 * 7);
const to = new Date(now + 1000 * 60 * 60 * 24 * 7);
return this.eventModel return this.eventModel
.find({ .find({
$and: [ $and: [
{ {
$or: [{ fromDateTime: [null] }, { fromDateTime: { $lte: now } }], $or: [{ fromDateTime: [null] }, { fromDateTime: { $lte: to } }],
}, },
{ {
$or: [{ toDateTime: [null] }, { toDateTime: { $gte: now } }], $or: [{ toDateTime: [null] }, { toDateTime: { $gte: from } }],
}, },
], ],
$nor: [{ isDeleted: true }, { isPrivate: true }], $nor: [{ isDeleted: true }, { isPrivate: true }],

View File

@ -12,7 +12,10 @@ export class UsersService {
) {} ) {}
create(createUserDto: CreateUserDto): Promise<User> { create(createUserDto: CreateUserDto): Promise<User> {
const user = new this.userModel(createUserDto); const user = new this.userModel({
...createUserDto,
passwordResetRequired: true,
});
return user.save(); return user.save();
} }

View File

@ -6,6 +6,7 @@ import { PrivateTag } from '@/components/private-tag';
import { ProgressBar } from '@/components/progress-bar'; import { ProgressBar } from '@/components/progress-bar';
import { Event } from '@/interfaces/event.interface'; import { Event } from '@/interfaces/event.interface';
import { User } from '@/interfaces/user.interface'; import { User } from '@/interfaces/user.interface';
import { getUTCString } from '@/util/date.util';
import { faMinus, faPlus } from '@fortawesome/free-solid-svg-icons'; import { faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import Link from 'next/link'; import Link from 'next/link';
@ -34,8 +35,8 @@ export function EventComponent({ event }: EventComponentProps) {
{event.fromDateTime && event.toDateTime && ( {event.fromDateTime && event.toDateTime && (
<div className="mt-4"> <div className="mt-4">
<div className="mb-2 flex justify-between"> <div className="mb-2 flex justify-between">
<div>{event.fromDateTime.toLocaleString()}</div> <div>{getUTCString(event.fromDateTime)}</div>
<div>{event.toDateTime.toLocaleString()}</div> <div>{getUTCString(event.toDateTime)}</div>
</div> </div>
<ProgressBar start={event.fromDateTime} end={event.toDateTime} /> <ProgressBar start={event.fromDateTime} end={event.toDateTime} />
</div> </div>

View File

@ -3,6 +3,7 @@
import { apiFunctions } from '@/api'; import { apiFunctions } from '@/api';
import { PrivateTag } from '@/components/private-tag'; import { PrivateTag } from '@/components/private-tag';
import { Event } from '@/interfaces/event.interface'; import { Event } from '@/interfaces/event.interface';
import { getUTCString } from '@/util/date.util';
import Link from 'next/link'; import Link from 'next/link';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
@ -33,8 +34,13 @@ export function EventsList() {
</td> </td>
<td className="flex-1 text-sm opacity-80"> <td className="flex-1 text-sm opacity-80">
<div>Od: {event.fromDateTime?.toLocaleDateString() ?? '/'}</div> <div>
<div>Do: {event.toDateTime?.toLocaleDateString() ?? '/'}</div> Od:{' '}
{event.fromDateTime ? getUTCString(event.fromDateTime) : '/'}
</div>
<div>
Do: {event.toDateTime ? getUTCString(event.toDateTime) : '/'}
</div>
</td> </td>
<th> <th>

View File

@ -1,5 +1,3 @@
'use client';
import { redirect } from 'next/navigation'; import { redirect } from 'next/navigation';
export default function AdminPage() { export default function AdminPage() {

View File

@ -1,27 +0,0 @@
'use client';
import { User } from '@/interfaces/user.interface';
interface DeleteUserDialogProps {
user: User | undefined;
onCancel: () => void;
onConfirm: () => void;
}
export function DeleteUserDialog({ user }: DeleteUserDialogProps) {
return (
<dialog id="delete_user" className="modal">
<div className="modal-box">
<h3 className="text-lg font-bold">Izbriši uporabnika</h3>
<p className="py-4">
Are you sure you want to deactivate the account &quot;
<strong className="text-black">{user?.username}</strong>
&quot;? This action cannot be undone.
</p>
</div>
<form method="dialog" className="modal-backdrop">
<button>close</button>
</form>
</dialog>
);
}

View File

@ -50,10 +50,8 @@ export function UsersList() {
<tr key={user._id}> <tr key={user._id}>
<td className="my-auto flex-1"> <td className="my-auto flex-1">
<div className="text-xl"> <div className="text-xl">
<span className="font-callsign"> <span className="font-callsign">{user.username}</span> -{' '}
{user.username.toUpperCase()} {user.name}
</span>{' '}
- {user.name}
</div>{' '} </div>{' '}
<div className="text-xs opacity-80">{user._id}</div> <div className="text-xs opacity-80">{user._id}</div>
</td> </td>
@ -115,6 +113,17 @@ export function UsersList() {
type="button" type="button"
className="btn btn-error" className="btn btn-error"
// onClick={onConfirm} // onClick={onConfirm}
onClick={() => {
apiFunctions
.deleteUser(deleteUser!._id)
.then(() => {
getUsers();
dialogRef.current?.close();
})
.catch((error) => {
console.error(error);
});
}}
> >
Izbriši Izbriši
</button> </button>
@ -122,7 +131,7 @@ export function UsersList() {
<button <button
type="button" type="button"
className="btn" className="btn"
// onClick={onCancel} onClick={() => dialogRef.current?.close()}
> >
Prekliči Prekliči
</button> </button>

View File

@ -11,6 +11,7 @@ import { User } from '@/interfaces/user.interface';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import Link from 'next/link'; import Link from 'next/link';
import { useUserState } from '@/state/user-state'; import { useUserState } from '@/state/user-state';
import { Role } from '@/enums/role.enum';
interface EventComponentProps { interface EventComponentProps {
event: Event; event: Event;
@ -27,12 +28,15 @@ export function EventComponent({ event }: EventComponentProps) {
return ( return (
<div className="flex flex-col gap-8"> <div className="flex flex-col gap-8">
<div> <div>
<div className="flex flex-col items-start gap-2"> <div className="flex justify-between">
<h1 className="font-callsign text-4xl font-medium"> <div className="flex flex-col items-start gap-2">
{event.callsign} <h1 className="font-callsign text-4xl font-medium">
</h1> {event.callsign}
<p className="opacity-90">{event.description}</p> </h1>
{event.isPrivate && <PrivateTag />} <p className="opacity-90">{event.description}</p>
{event.isPrivate && <PrivateTag />}
</div>
<EditButton id={event._id} />
</div> </div>
{event.fromDateTime && event.toDateTime && ( {event.fromDateTime && event.toDateTime && (
@ -76,3 +80,16 @@ export function EventComponent({ event }: EventComponentProps) {
</div> </div>
); );
} }
function EditButton({ id }: { id: string }) {
const user = useUserState((s) => s.user);
console.log(user);
if (!user || !user.roles.includes(Role.Admin)) return null;
return (
<Link href={`/admin/events/${id}`} className="btn btn-warning btn-sm">
Uredi
</Link>
);
}

View File

@ -2,7 +2,13 @@
import { apiFunctions } from '@/api'; import { apiFunctions } from '@/api';
import { Event } from '@/interfaces/event.interface'; import { Event } from '@/interfaces/event.interface';
import { Reservation } from '@/interfaces/reservation.interface'; import { Reservation } from '@/interfaces/reservation.interface';
import { dayInMs, dayInWeeks, getNextNDays } from '@/util/date.util'; import {
dayInMs,
dayInWeeks,
getNextNDays,
getUTCDMString,
getUTCDateString,
} from '@/util/date.util';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Band } from '@/enums/band.enum'; import { Band } from '@/enums/band.enum';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@ -22,6 +28,7 @@ export function FreeDatesComponent({ event }: { event: Event }) {
const end = dates[dates.length - 1]?.toISOString(); const end = dates[dates.length - 1]?.toISOString();
useEffect(() => { useEffect(() => {
setReservations(undefined);
apiFunctions apiFunctions
.getReservationsForEvent(event._id, { .getReservationsForEvent(event._id, {
start, start,
@ -31,7 +38,9 @@ export function FreeDatesComponent({ event }: { event: Event }) {
.catch(console.error); .catch(console.error);
}, [event._id, start, end]); }, [event._id, start, end]);
const freeTable = bands.map(() => dates.map(() => true)); const freeTable = bands.map(() =>
dates.map<boolean | null>(() => (reservations == undefined ? null : true)),
);
for (const reservation of reservations ?? []) { for (const reservation of reservations ?? []) {
const forDateDay = reservation.forDate.valueOf() / dayInMs; const forDateDay = reservation.forDate.valueOf() / dayInMs;
@ -56,8 +65,10 @@ export function FreeDatesComponent({ event }: { event: Event }) {
</button> </button>
<div className="join-item flex w-full bg-base-200"> <div className="join-item flex w-full bg-base-200">
<div className="m-auto"> <div className="m-auto">
{dates[0]?.toLocaleDateString('sl')} -{' '} {dates[0] ? getUTCDateString(dates[0]) : ''} -{' '}
{dates[dates.length - 1]?.toLocaleDateString('sl')} {dates[dates.length - 1]
? getUTCDateString(dates[dates.length - 1])
: ''}
</div> </div>
</div> </div>
<button <button
@ -85,9 +96,7 @@ export function FreeDatesComponent({ event }: { event: Event }) {
{dates.map((date, i) => ( {dates.map((date, i) => (
<th key={i} className="px-3"> <th key={i} className="px-3">
<div>{dayInWeeks[date.getUTCDay()]}</div> <div>{dayInWeeks[date.getUTCDay()]}</div>
<div> <div>{getUTCDMString(date)}</div>
{date.getUTCDate()}. {date.getUTCMonth() + 1}.
</div>
</th> </th>
))} ))}
</tr> </tr>
@ -105,7 +114,11 @@ export function FreeDatesComponent({ event }: { event: Event }) {
className={`m-auto h-3 w-full${ className={`m-auto h-3 w-full${
i > 0 ? '' : ' rounded-l-full' i > 0 ? '' : ' rounded-l-full'
}${i == dates.length - 1 ? ' rounded-r-full' : ''} ${ }${i == dates.length - 1 ? ' rounded-r-full' : ''} ${
isFree ? 'bg-green-500' : 'bg-red-500' isFree == null
? 'bg-base-200'
: isFree
? 'bg-green-500/90'
: 'bg-red-500/90'
}`} }`}
/> />
</td> </td>

View File

@ -6,7 +6,7 @@ import { Mode } from '@/enums/mode.enum';
import { Event } from '@/interfaces/event.interface'; import { Event } from '@/interfaces/event.interface';
import { User } from '@/interfaces/user.interface'; import { User } from '@/interfaces/user.interface';
import { useUserState } from '@/state/user-state'; import { useUserState } from '@/state/user-state';
import { getNextNDays } from '@/util/date.util'; import { getNextNDays, getUTCDMString } from '@/util/date.util';
import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons'; import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
@ -85,7 +85,7 @@ export function ReserveComponent({ event }: ReserveComponentProps) {
date?.valueOf() === dt?.valueOf() ? 'btn-primary' : '' date?.valueOf() === dt?.valueOf() ? 'btn-primary' : ''
}`} }`}
> >
{dt.getDate()}. {dt.getMonth() + 1}. {getUTCDMString(dt)}
</button> </button>
))} ))}
</div> </div>

View File

@ -1,9 +1,8 @@
'use client'; 'use client';
import { Header } from '@/components/header'; import { Header } from '@/components/header';
import { THEME_DARK, useThemeState } from '@/state/theme-state'; import { THEME_LIGHT } from '@/state/theme-state';
import { Allerta, Inter } from 'next/font/google'; import { Allerta, Inter } from 'next/font/google';
import { useEffect, useState } from 'react';
const callsignFont = Allerta({ const callsignFont = Allerta({
subsets: ['latin'], subsets: ['latin'],
@ -14,15 +13,15 @@ const callsignFont = Allerta({
const inter = Inter({ subsets: ['latin'] }); const inter = Inter({ subsets: ['latin'] });
export function LayoutComponent({ children }: { children: React.ReactNode }) { export function LayoutComponent({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<string>(THEME_DARK); // const [theme, setTheme] = useState<string>(THEME_DARK);
useEffect(() => { // useEffect(() => {
setTheme(useThemeState.getState().theme); // setTheme(useThemeState.getState().theme);
useThemeState.subscribe((s) => setTheme(s.theme)); // useThemeState.subscribe((s) => setTheme(s.theme));
}, []); // }, []);
return ( return (
<html lang="sl" data-theme={theme}> <html lang="sl" data-theme={THEME_LIGHT}>
<body <body
className={`${inter.className} ${callsignFont.variable} dark:[color-scheme:dark]`} className={`${inter.className} ${callsignFont.variable} dark:[color-scheme:dark]`}
> >

View File

@ -4,7 +4,7 @@ import { apiFunctions } from '@/api';
import { Event } from '@/interfaces/event.interface'; import { Event } from '@/interfaces/event.interface';
import { LogSummary } from '@/interfaces/log-summary.interface'; import { LogSummary } from '@/interfaces/log-summary.interface';
import { Reservation } from '@/interfaces/reservation.interface'; import { Reservation } from '@/interfaces/reservation.interface';
import { getUTCString } from '@/util/date.util'; import { getUTCDateString, getUTCString } from '@/util/date.util';
import { import {
faFileCircleCheck, faFileCircleCheck,
faFileCircleExclamation, faFileCircleExclamation,
@ -27,7 +27,7 @@ export function ReservationComponent({
<h1 className="font-callsign text-3xl">{event.callsign}</h1> <h1 className="font-callsign text-3xl">{event.callsign}</h1>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<div>Datum: {reservation.forDate.toISOString().slice(0, 10)}</div> <div>Datum: {getUTCDateString(reservation.forDate)}</div>
<div>Frekvenčni pasovi: {reservation.bands.join(', ')}</div> <div>Frekvenčni pasovi: {reservation.bands.join(', ')}</div>
<div>Način dela: {reservation.modes.join(', ')}</div> <div>Način dela: {reservation.modes.join(', ')}</div>
</div> </div>

View File

@ -1,5 +1,6 @@
import { Event } from '@/interfaces/event.interface'; import { Event } from '@/interfaces/event.interface';
import { ProgressBar } from './progress-bar'; import { ProgressBar } from './progress-bar';
import { getUTCDateString, getUTCTimeString } from '@/util/date.util';
interface EventCardProps { interface EventCardProps {
event: Event; event: Event;
@ -7,7 +8,7 @@ interface EventCardProps {
export function EventCard({ event }: EventCardProps) { export function EventCard({ event }: EventCardProps) {
return ( return (
<div className="card bg-base-100 flex h-full flex-col justify-between gap-3 border border-primary shadow-xl"> <div className="card flex h-full flex-col justify-between gap-3 border border-neutral-200 shadow-lg dark:border-0 dark:bg-neutral dark:text-neutral-content">
<div className="card-body"> <div className="card-body">
<div> <div>
<h1 className="font-callsign card-title mb-1 text-2xl"> <h1 className="font-callsign card-title mb-1 text-2xl">
@ -19,24 +20,35 @@ export function EventCard({ event }: EventCardProps) {
) : ( ) : (
<p className="font-light italic">Brez opisa</p> <p className="font-light italic">Brez opisa</p>
)} )}
{event.fromDateTime && event.fromDateTime > new Date() && (
<div className="badge badge-primary mt-3 w-full">Začetek kmalu</div>
)}
{event.toDateTime && event.toDateTime < new Date() && (
<div className="badge badge-ghost mt-3 w-full">Zaključeno</div>
)}
</div> </div>
{(event.fromDateTime != undefined) !== {(event.fromDateTime != undefined) !==
(event.toDateTime != undefined) && ( (event.toDateTime != undefined) && (
<div> <div className="mt-2 text-sm">
{event.fromDateTime ? 'Od' : 'Do'}:{' '} <span className="font-bold">
{(event.fromDateTime ?? event.toDateTime!).toLocaleDateString()} {event.fromDateTime ? 'Od' : 'Do'}:
</span>{' '}
{getUTCDateString(event.fromDateTime ?? event.toDateTime!)}{' '}
{getUTCTimeString(event.fromDateTime ?? event.toDateTime!)}
</div> </div>
)} )}
{event.fromDateTime && event.toDateTime && ( {event.fromDateTime && event.toDateTime && (
<div className="mt-2"> <div className="mt-2 text-sm">
<div className="flex justify-between text-sm"> <div className="flex justify-between">
{event.fromDateTime && ( <div>{getUTCDateString(event.fromDateTime)}</div>
<div>{event.fromDateTime.toLocaleDateString()}</div> <div>{getUTCDateString(event.toDateTime)}</div>
)} </div>
{event.toDateTime && ( <div className="flex justify-between">
<div>{event.toDateTime.toLocaleDateString()}</div> <div>{getUTCTimeString(event.fromDateTime)}</div>
)} <div>{getUTCTimeString(event.toDateTime)}</div>
</div> </div>
<ProgressBar start={event.fromDateTime} end={event.toDateTime} /> <ProgressBar start={event.fromDateTime} end={event.toDateTime} />

View File

@ -2,29 +2,28 @@
import { Role } from '@/enums/role.enum'; import { Role } from '@/enums/role.enum';
import { useAuthState } from '@/state/auth-state'; import { useAuthState } from '@/state/auth-state';
import { THEME_DARK, useThemeState } from '@/state/theme-state';
import { useUserState } from '@/state/user-state'; import { useUserState } from '@/state/user-state';
import { faMoon, faSun, faUserCircle } from '@fortawesome/free-solid-svg-icons'; import { faUserCircle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import Link from 'next/link'; import Link from 'next/link';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
export function Header() { export function Header() {
const [theme, setTheme] = useState<string>(THEME_DARK); // const [theme, setTheme] = useState<string>(THEME_DARK);
const toggleTheme = useThemeState((s) => s.toggleTheme); // const toggleTheme = useThemeState((s) => s.toggleTheme);
useEffect(() => { // useEffect(() => {
setTheme(useThemeState.getState().theme); // setTheme(useThemeState.getState().theme);
useThemeState.subscribe((s) => setTheme(s.theme)); // useThemeState.subscribe((s) => setTheme(s.theme));
}, []); // }, []);
return ( return (
<div className="flex h-16 select-none justify-between border-b border-primary"> <div className="flex h-16 select-none justify-between bg-primary text-primary-content shadow-md dark:border-b dark:border-primary">
<Link href="/" className="my-auto ml-4 text-2xl font-semibold"> <Link href="/" className="my-auto ml-4 text-2xl font-semibold">
Ham Reserve Ham Reserve
</Link> </Link>
<div className="flex"> <div className="flex">
<label className="header-button swap swap-rotate"> {/* <label className="header-button swap-rotate swap">
<input <input
type="checkbox" type="checkbox"
checked={theme === THEME_DARK} checked={theme === THEME_DARK}
@ -42,7 +41,7 @@ export function Header() {
height={20} height={20}
className="swap-on h-5 w-5" className="swap-on h-5 w-5"
/> />
</label> </label> */}
<UserHeader /> <UserHeader />
</div> </div>
</div> </div>
@ -74,11 +73,12 @@ function UserHeader() {
</label> </label>
<div <div
className={`absolute right-2 top-full z-[1] pt-4 ${ className={`absolute right-2 top-full z-[1] pt-1 ${
isOpen ? '' : 'hidden' isOpen ? '' : 'hidden'
}`} }`}
> >
<ul className="menu flex w-60 flex-col gap-1 rounded-xl bg-base-100 p-2 text-base-content shadow-md"> <div className="fixed inset-0" onClick={() => setIsOpen(false)} />
<ul className="menu flex w-60 flex-col gap-1 rounded-xl bg-base-100 p-2 text-base-content shadow-md dark:bg-base-200">
<li> <li>
<Link href="/profile" onClick={() => setIsOpen(false)}> <Link href="/profile" onClick={() => setIsOpen(false)}>
Profil Profil

View File

@ -13,5 +13,11 @@ export function ProgressBar({ start, end }: ProgressBarProps) {
), ),
).toFixed(1); ).toFixed(1);
return <progress className="progress" value={progress} max={100} />; return (
<progress
className="progress progress-primary"
value={progress}
max={100}
/>
);
} }

View File

@ -1,4 +1,5 @@
import { Reservation } from '@/interfaces/reservation.interface'; import { Reservation } from '@/interfaces/reservation.interface';
import { getUTCDateString } from '@/util/date.util';
import { import {
faFileCircleCheck, faFileCircleCheck,
faFileCircleExclamation, faFileCircleExclamation,
@ -30,7 +31,7 @@ export function ReservationsTable({ reservations }: ReservationsTableProps) {
<tbody> <tbody>
{reservations.map((reservation) => ( {reservations.map((reservation) => (
<tr key={reservation._id}> <tr key={reservation._id}>
<td>{reservation.forDate.toISOString().slice(0, 10)}</td> <td>{getUTCDateString(reservation.forDate)}</td>
<td> <td>
<div className="flex flex-wrap gap-1"> <div className="flex flex-wrap gap-1">
{reservation.bands.join(', ')} {reservation.bands.join(', ')}

View File

@ -1,5 +1,6 @@
import { apiFunctions } from '@/api'; import { apiFunctions } from '@/api';
import AsyncLock from 'async-lock'; import AsyncLock from 'async-lock';
import { AxiosError } from 'axios';
import jwtDecode from 'jwt-decode'; import jwtDecode from 'jwt-decode';
import secureLocalStorage from 'react-secure-storage'; import secureLocalStorage from 'react-secure-storage';
import { create } from 'zustand'; import { create } from 'zustand';
@ -36,7 +37,6 @@ export const useAuthState = create(
const { exp } = jwtDecode(accessToken) as { exp: number }; const { exp } = jwtDecode(accessToken) as { exp: number };
if (Date.now() < exp * 1000) { if (Date.now() < exp * 1000) {
return true; return true;
return;
} else set({ accessToken: null }); } else set({ accessToken: null });
} }
@ -48,12 +48,12 @@ export const useAuthState = create(
set(await apiFunctions.refresh(refreshToken)); set(await apiFunctions.refresh(refreshToken));
return true; return true;
} catch (e) { } catch (e) {
set({ refreshToken: null }); if (e instanceof AxiosError && e.response?.status === 401)
set({ refreshToken: null });
} }
} }
} }
set({ refreshToken: null, accessToken: null });
return false; return false;
}); });
}, },

View File

@ -1,16 +1,32 @@
import { Event } from '@/interfaces/event.interface'; import { Event } from '@/interfaces/event.interface';
export function getUTCString(dt: Date) { function pad(num: number) {
function pad(num: number) { return num < 10 ? '0' + num : num;
return num < 10 ? '0' + num : num; }
}
export function getUTCDMString(dt: Date) {
const month = pad(dt.getUTCMonth() + 1);
const date = pad(dt.getUTCDate());
return `${date}. ${month}.`;
}
export function getUTCDateString(dt: Date) {
const year = dt.getUTCFullYear(); const year = dt.getUTCFullYear();
const month = pad(dt.getUTCMonth() + 1); const month = pad(dt.getUTCMonth() + 1);
const date = pad(dt.getUTCDate()); const date = pad(dt.getUTCDate());
return `${date}. ${month}. ${year}`;
}
export function getUTCTimeString(dt: Date) {
const hour = pad(dt.getUTCHours()); const hour = pad(dt.getUTCHours());
const minute = pad(dt.getUTCMinutes()); const minute = pad(dt.getUTCMinutes());
return `${year}-${month}-${date} ${hour}:${minute} UTC`; return `${hour}:${minute}`;
}
export function getUTCString(dt: Date) {
const date = getUTCDateString(dt);
const time = getUTCTimeString(dt);
return `${date} ${time} UTC`;
} }
export const dayInMs = 1000 * 60 * 60 * 24; export const dayInMs = 1000 * 60 * 60 * 24;