diff --git a/nest-api/src/events/events.service.ts b/nest-api/src/events/events.service.ts index f74cea0..b15c189 100644 --- a/nest-api/src/events/events.service.ts +++ b/nest-api/src/events/events.service.ts @@ -21,15 +21,17 @@ export class EventsService { } findCurrent(): Promise { - 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 .find({ $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 }], diff --git a/nest-api/src/users/users.service.ts b/nest-api/src/users/users.service.ts index 2db9ae2..e119df3 100644 --- a/nest-api/src/users/users.service.ts +++ b/nest-api/src/users/users.service.ts @@ -12,7 +12,10 @@ export class UsersService { ) {} create(createUserDto: CreateUserDto): Promise { - const user = new this.userModel(createUserDto); + const user = new this.userModel({ + ...createUserDto, + passwordResetRequired: true, + }); return user.save(); } diff --git a/next-app/src/app/admin/events/[id]/event-component.tsx b/next-app/src/app/admin/events/[id]/event-component.tsx index d8b547b..2530f3d 100644 --- a/next-app/src/app/admin/events/[id]/event-component.tsx +++ b/next-app/src/app/admin/events/[id]/event-component.tsx @@ -6,6 +6,7 @@ import { PrivateTag } from '@/components/private-tag'; import { ProgressBar } from '@/components/progress-bar'; import { Event } from '@/interfaces/event.interface'; import { User } from '@/interfaces/user.interface'; +import { getUTCString } from '@/util/date.util'; import { faMinus, faPlus } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import Link from 'next/link'; @@ -34,8 +35,8 @@ export function EventComponent({ event }: EventComponentProps) { {event.fromDateTime && event.toDateTime && (
-
{event.fromDateTime.toLocaleString()}
-
{event.toDateTime.toLocaleString()}
+
{getUTCString(event.fromDateTime)}
+
{getUTCString(event.toDateTime)}
diff --git a/next-app/src/app/admin/events/events-list.tsx b/next-app/src/app/admin/events/events-list.tsx index 3167435..739d4e5 100644 --- a/next-app/src/app/admin/events/events-list.tsx +++ b/next-app/src/app/admin/events/events-list.tsx @@ -3,6 +3,7 @@ import { apiFunctions } from '@/api'; import { PrivateTag } from '@/components/private-tag'; import { Event } from '@/interfaces/event.interface'; +import { getUTCString } from '@/util/date.util'; import Link from 'next/link'; import { useEffect, useState } from 'react'; @@ -33,8 +34,13 @@ export function EventsList() { -
Od: {event.fromDateTime?.toLocaleDateString() ?? '/'}
-
Do: {event.toDateTime?.toLocaleDateString() ?? '/'}
+
+ Od:{' '} + {event.fromDateTime ? getUTCString(event.fromDateTime) : '/'} +
+
+ Do: {event.toDateTime ? getUTCString(event.toDateTime) : '/'} +
diff --git a/next-app/src/app/admin/page.tsx b/next-app/src/app/admin/page.tsx index 9b8e69f..4c5283c 100644 --- a/next-app/src/app/admin/page.tsx +++ b/next-app/src/app/admin/page.tsx @@ -1,5 +1,3 @@ -'use client'; - import { redirect } from 'next/navigation'; export default function AdminPage() { diff --git a/next-app/src/app/admin/users/delete-user-dialog.tsx b/next-app/src/app/admin/users/delete-user-dialog.tsx deleted file mode 100644 index fbe5803..0000000 --- a/next-app/src/app/admin/users/delete-user-dialog.tsx +++ /dev/null @@ -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 ( - -
-

Izbriši uporabnika

-

- Are you sure you want to deactivate the account " - {user?.username} - "? This action cannot be undone. -

-
-
- -
-
- ); -} diff --git a/next-app/src/app/admin/users/users-list.tsx b/next-app/src/app/admin/users/users-list.tsx index f973eed..44b0a05 100644 --- a/next-app/src/app/admin/users/users-list.tsx +++ b/next-app/src/app/admin/users/users-list.tsx @@ -50,10 +50,8 @@ export function UsersList() {
- - {user.username.toUpperCase()} - {' '} - - {user.name} + {user.username} -{' '} + {user.name}
{' '}
{user._id}
@@ -115,6 +113,17 @@ export function UsersList() { type="button" className="btn btn-error" // onClick={onConfirm} + onClick={() => { + apiFunctions + .deleteUser(deleteUser!._id) + .then(() => { + getUsers(); + dialogRef.current?.close(); + }) + .catch((error) => { + console.error(error); + }); + }} > Izbriši @@ -122,7 +131,7 @@ export function UsersList() { diff --git a/next-app/src/app/event/[id]/event-component.tsx b/next-app/src/app/event/[id]/event-component.tsx index dbd09c3..dbe08a8 100644 --- a/next-app/src/app/event/[id]/event-component.tsx +++ b/next-app/src/app/event/[id]/event-component.tsx @@ -11,6 +11,7 @@ import { User } from '@/interfaces/user.interface'; import { useEffect, useState } from 'react'; import Link from 'next/link'; import { useUserState } from '@/state/user-state'; +import { Role } from '@/enums/role.enum'; interface EventComponentProps { event: Event; @@ -27,12 +28,15 @@ export function EventComponent({ event }: EventComponentProps) { return (
-
-

- {event.callsign} -

-

{event.description}

- {event.isPrivate && } +
+
+

+ {event.callsign} +

+

{event.description}

+ {event.isPrivate && } +
+
{event.fromDateTime && event.toDateTime && ( @@ -76,3 +80,16 @@ export function EventComponent({ event }: EventComponentProps) {
); } + +function EditButton({ id }: { id: string }) { + const user = useUserState((s) => s.user); + console.log(user); + + if (!user || !user.roles.includes(Role.Admin)) return null; + + return ( + + Uredi + + ); +} diff --git a/next-app/src/app/event/[id]/free-dates-component.tsx b/next-app/src/app/event/[id]/free-dates-component.tsx index c651a18..ee9eb1d 100644 --- a/next-app/src/app/event/[id]/free-dates-component.tsx +++ b/next-app/src/app/event/[id]/free-dates-component.tsx @@ -2,7 +2,13 @@ import { apiFunctions } from '@/api'; import { Event } from '@/interfaces/event.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 { Band } from '@/enums/band.enum'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -22,6 +28,7 @@ export function FreeDatesComponent({ event }: { event: Event }) { const end = dates[dates.length - 1]?.toISOString(); useEffect(() => { + setReservations(undefined); apiFunctions .getReservationsForEvent(event._id, { start, @@ -31,7 +38,9 @@ export function FreeDatesComponent({ event }: { event: Event }) { .catch(console.error); }, [event._id, start, end]); - const freeTable = bands.map(() => dates.map(() => true)); + const freeTable = bands.map(() => + dates.map(() => (reservations == undefined ? null : true)), + ); for (const reservation of reservations ?? []) { const forDateDay = reservation.forDate.valueOf() / dayInMs; @@ -56,8 +65,10 @@ export function FreeDatesComponent({ event }: { event: Event }) {
- {dates[0]?.toLocaleDateString('sl')} -{' '} - {dates[dates.length - 1]?.toLocaleDateString('sl')} + {dates[0] ? getUTCDateString(dates[0]) : ''} -{' '} + {dates[dates.length - 1] + ? getUTCDateString(dates[dates.length - 1]) + : ''}
))}
diff --git a/next-app/src/app/layout-component.tsx b/next-app/src/app/layout-component.tsx index b125e3f..709e46b 100644 --- a/next-app/src/app/layout-component.tsx +++ b/next-app/src/app/layout-component.tsx @@ -1,9 +1,8 @@ 'use client'; 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 { useEffect, useState } from 'react'; const callsignFont = Allerta({ subsets: ['latin'], @@ -14,15 +13,15 @@ const callsignFont = Allerta({ const inter = Inter({ subsets: ['latin'] }); export function LayoutComponent({ children }: { children: React.ReactNode }) { - const [theme, setTheme] = useState(THEME_DARK); + // const [theme, setTheme] = useState(THEME_DARK); - useEffect(() => { - setTheme(useThemeState.getState().theme); - useThemeState.subscribe((s) => setTheme(s.theme)); - }, []); + // useEffect(() => { + // setTheme(useThemeState.getState().theme); + // useThemeState.subscribe((s) => setTheme(s.theme)); + // }, []); return ( - + diff --git a/next-app/src/app/reservation/[id]/reservation-component.tsx b/next-app/src/app/reservation/[id]/reservation-component.tsx index 6e36d60..2a568e4 100644 --- a/next-app/src/app/reservation/[id]/reservation-component.tsx +++ b/next-app/src/app/reservation/[id]/reservation-component.tsx @@ -4,7 +4,7 @@ import { apiFunctions } from '@/api'; import { Event } from '@/interfaces/event.interface'; import { LogSummary } from '@/interfaces/log-summary.interface'; import { Reservation } from '@/interfaces/reservation.interface'; -import { getUTCString } from '@/util/date.util'; +import { getUTCDateString, getUTCString } from '@/util/date.util'; import { faFileCircleCheck, faFileCircleExclamation, @@ -27,7 +27,7 @@ export function ReservationComponent({

{event.callsign}

-
Datum: {reservation.forDate.toISOString().slice(0, 10)}
+
Datum: {getUTCDateString(reservation.forDate)}
Frekvenčni pasovi: {reservation.bands.join(', ')}
Način dela: {reservation.modes.join(', ')}
diff --git a/next-app/src/components/event-card.tsx b/next-app/src/components/event-card.tsx index eac0b46..f00cedf 100644 --- a/next-app/src/components/event-card.tsx +++ b/next-app/src/components/event-card.tsx @@ -1,5 +1,6 @@ import { Event } from '@/interfaces/event.interface'; import { ProgressBar } from './progress-bar'; +import { getUTCDateString, getUTCTimeString } from '@/util/date.util'; interface EventCardProps { event: Event; @@ -7,7 +8,7 @@ interface EventCardProps { export function EventCard({ event }: EventCardProps) { return ( -
+

@@ -19,24 +20,35 @@ export function EventCard({ event }: EventCardProps) { ) : (

Brez opisa

)} + + {event.fromDateTime && event.fromDateTime > new Date() && ( +
Začetek kmalu
+ )} + + {event.toDateTime && event.toDateTime < new Date() && ( +
Zaključeno
+ )}

{(event.fromDateTime != undefined) !== (event.toDateTime != undefined) && ( -
- {event.fromDateTime ? 'Od' : 'Do'}:{' '} - {(event.fromDateTime ?? event.toDateTime!).toLocaleDateString()} +
+ + {event.fromDateTime ? 'Od' : 'Do'}: + {' '} + {getUTCDateString(event.fromDateTime ?? event.toDateTime!)}{' '} + {getUTCTimeString(event.fromDateTime ?? event.toDateTime!)}
)} {event.fromDateTime && event.toDateTime && ( -
-
- {event.fromDateTime && ( -
{event.fromDateTime.toLocaleDateString()}
- )} - {event.toDateTime && ( -
{event.toDateTime.toLocaleDateString()}
- )} +
+
+
{getUTCDateString(event.fromDateTime)}
+
{getUTCDateString(event.toDateTime)}
+
+
+
{getUTCTimeString(event.fromDateTime)}
+
{getUTCTimeString(event.toDateTime)}
diff --git a/next-app/src/components/header.tsx b/next-app/src/components/header.tsx index a32f9fd..db3e0a5 100644 --- a/next-app/src/components/header.tsx +++ b/next-app/src/components/header.tsx @@ -2,29 +2,28 @@ import { Role } from '@/enums/role.enum'; import { useAuthState } from '@/state/auth-state'; -import { THEME_DARK, useThemeState } from '@/state/theme-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 Link from 'next/link'; import { useEffect, useState } from 'react'; export function Header() { - const [theme, setTheme] = useState(THEME_DARK); - const toggleTheme = useThemeState((s) => s.toggleTheme); + // const [theme, setTheme] = useState(THEME_DARK); + // const toggleTheme = useThemeState((s) => s.toggleTheme); - useEffect(() => { - setTheme(useThemeState.getState().theme); - useThemeState.subscribe((s) => setTheme(s.theme)); - }, []); + // useEffect(() => { + // setTheme(useThemeState.getState().theme); + // useThemeState.subscribe((s) => setTheme(s.theme)); + // }, []); return ( -
+
Ham Reserve
- */}
@@ -74,11 +73,12 @@ function UserHeader() {
-
    +
    setIsOpen(false)} /> +
    • setIsOpen(false)}> Profil diff --git a/next-app/src/components/progress-bar.tsx b/next-app/src/components/progress-bar.tsx index 448d80a..322a869 100644 --- a/next-app/src/components/progress-bar.tsx +++ b/next-app/src/components/progress-bar.tsx @@ -13,5 +13,11 @@ export function ProgressBar({ start, end }: ProgressBarProps) { ), ).toFixed(1); - return ; + return ( + + ); } diff --git a/next-app/src/components/reservations-table.tsx b/next-app/src/components/reservations-table.tsx index f3eb733..a5e9c1e 100644 --- a/next-app/src/components/reservations-table.tsx +++ b/next-app/src/components/reservations-table.tsx @@ -1,4 +1,5 @@ import { Reservation } from '@/interfaces/reservation.interface'; +import { getUTCDateString } from '@/util/date.util'; import { faFileCircleCheck, faFileCircleExclamation, @@ -30,7 +31,7 @@ export function ReservationsTable({ reservations }: ReservationsTableProps) { {reservations.map((reservation) => ( - {reservation.forDate.toISOString().slice(0, 10)} + {getUTCDateString(reservation.forDate)}
      {reservation.bands.join(', ')} diff --git a/next-app/src/state/auth-state.ts b/next-app/src/state/auth-state.ts index b1a167d..65162f1 100644 --- a/next-app/src/state/auth-state.ts +++ b/next-app/src/state/auth-state.ts @@ -1,5 +1,6 @@ import { apiFunctions } from '@/api'; import AsyncLock from 'async-lock'; +import { AxiosError } from 'axios'; import jwtDecode from 'jwt-decode'; import secureLocalStorage from 'react-secure-storage'; import { create } from 'zustand'; @@ -36,7 +37,6 @@ export const useAuthState = create( const { exp } = jwtDecode(accessToken) as { exp: number }; if (Date.now() < exp * 1000) { return true; - return; } else set({ accessToken: null }); } @@ -48,12 +48,12 @@ export const useAuthState = create( set(await apiFunctions.refresh(refreshToken)); return true; } catch (e) { - set({ refreshToken: null }); + if (e instanceof AxiosError && e.response?.status === 401) + set({ refreshToken: null }); } } } - set({ refreshToken: null, accessToken: null }); return false; }); }, diff --git a/next-app/src/util/date.util.ts b/next-app/src/util/date.util.ts index 7e9d449..9d30c75 100644 --- a/next-app/src/util/date.util.ts +++ b/next-app/src/util/date.util.ts @@ -1,16 +1,32 @@ import { Event } from '@/interfaces/event.interface'; -export function getUTCString(dt: Date) { - function pad(num: number) { - return num < 10 ? '0' + num : num; - } +function pad(num: number) { + 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 month = pad(dt.getUTCMonth() + 1); const date = pad(dt.getUTCDate()); + return `${date}. ${month}. ${year}`; +} + +export function getUTCTimeString(dt: Date) { const hour = pad(dt.getUTCHours()); 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;