diff --git a/rogue-thi-app/components/cards/FoodCard.jsx b/rogue-thi-app/components/cards/FoodCard.jsx index cc81edf2..9df1a3e4 100644 --- a/rogue-thi-app/components/cards/FoodCard.jsx +++ b/rogue-thi-app/components/cards/FoodCard.jsx @@ -6,9 +6,12 @@ import { faUtensils } from '@fortawesome/free-solid-svg-icons' import { Trans, useTranslation } from 'next-i18next' import BaseCard from './BaseCard' import { FoodFilterContext } from '../../pages/_app' +import { TextBlock } from 'react-placeholder/lib/placeholders' import { formatISODate } from '../../lib/date-utils' import { loadFoodEntries } from '../../lib/backend-utils/food-utils' +import styles from '../../styles/Home.module.css' + /** * Dashboard card for Mensa and Reimanns food plans. */ @@ -87,13 +90,24 @@ export default function FoodCard () { load() }, [selectedRestaurants, preferencesSelection, allergenSelection, t, i18n]) + const placeholder = ( + + + + + + + + + ) + return ( - + {foodEntries && foodEntries.map((x, i) => ( diff --git a/rogue-thi-app/components/cards/MobilityCard.jsx b/rogue-thi-app/components/cards/MobilityCard.jsx index 701a3c02..6601dfa0 100644 --- a/rogue-thi-app/components/cards/MobilityCard.jsx +++ b/rogue-thi-app/components/cards/MobilityCard.jsx @@ -11,6 +11,7 @@ import { import { RenderMobilityEntry, + RenderMobilityEntryPlaceholder, getMobilityEntries, getMobilityLabel, getMobilitySettings @@ -66,13 +67,23 @@ export default function MobilityCard () { load() }, [mobilitySettings, time, t]) + const placeholder = ( + + {Array.from({ length: 4 }, (_, i) => ( + + + + ))} + + ) + return ( - + {mobility && mobility.slice(0, 4).map((entry, i) => diff --git a/rogue-thi-app/components/cards/RoomCard.jsx b/rogue-thi-app/components/cards/RoomCard.jsx index bc24bdeb..3d087f09 100644 --- a/rogue-thi-app/components/cards/RoomCard.jsx +++ b/rogue-thi-app/components/cards/RoomCard.jsx @@ -10,7 +10,9 @@ import { findSuggestedRooms, getEmptySuggestions, getTranslatedRoomName } from ' import { formatFriendlyTime, isSameDay } from '../../lib/date-utils' import { getFriendlyTimetable, getTimetableGaps } from '../../lib/backend-utils/timetable-utils' import { NoSessionError } from '../../lib/backend/thi-session-handler' + import ReactPlaceholder from 'react-placeholder/lib' +import { TextBlock } from 'react-placeholder/lib/placeholders' import { USER_STUDENT, useUserKind } from '../../lib/hooks/user-kind' import Link from 'next/link' @@ -70,13 +72,23 @@ export default function RoomCard () { } }, [router, userKind]) + const placeholder = ( + + {Array.from({ length: 2 }, (_, i) => ( + + + + ))} + + ) + return ( - + {filterResults && filterResults.slice(0, 2).map((x, i) => { return ( diff --git a/rogue-thi-app/components/cards/TimetableCard.jsx b/rogue-thi-app/components/cards/TimetableCard.jsx index 7bc27e9e..36868d84 100644 --- a/rogue-thi-app/components/cards/TimetableCard.jsx +++ b/rogue-thi-app/components/cards/TimetableCard.jsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react' import ListGroup from 'react-bootstrap/ListGroup' import ReactPlaceholder from 'react-placeholder' +import { TextBlock } from 'react-placeholder/lib/placeholders' import { useRouter } from 'next/router' import { faCalendarMinus } from '@fortawesome/free-solid-svg-icons' @@ -11,6 +12,8 @@ import BaseCard from './BaseCard' import { NoSessionError } from '../../lib/backend/thi-session-handler' import { useTranslation } from 'next-i18next' +import styles from '../../styles/Home.module.css' + /** * Dashboard card for the timetable. */ @@ -42,13 +45,25 @@ export default function TimetableCard () { return () => clearInterval(interval) }, []) + const placeholder = ( + <> + + {Array.from({ length: 2 }, (_, i) => ( + + + + ))} + + + ) + return ( - + {(timetable && timetable.slice(0, 2).map((x, i) => { const isSoon = (x.startDate > currentTime && new Date(x.startDate) <= new Date(currentTime.getTime() + 30 * 60 * 1000)) diff --git a/rogue-thi-app/lib/backend-utils/mobility-utils.jsx b/rogue-thi-app/lib/backend-utils/mobility-utils.jsx index f39c31fa..acb5b2ca 100644 --- a/rogue-thi-app/lib/backend-utils/mobility-utils.jsx +++ b/rogue-thi-app/lib/backend-utils/mobility-utils.jsx @@ -2,6 +2,8 @@ import { faEuroSign, faKey } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCreativeCommonsNcEu } from '@fortawesome/free-brands-svg-icons' +import { TextBlock } from 'react-placeholder/lib/placeholders' + import { formatFriendlyTime, formatRelativeMinutes } from '../date-utils' import API from '../backend/authenticated-api' import NeulandAPI from '../backend/neuland-api' @@ -108,6 +110,29 @@ export async function getMobilityEntries (kind, station) { } } +export function RenderMobilityEntryPlaceholder ({ kind, styles }) { + if (kind === 'charging') { + return ( + <> +
+ +
+ + ) + } + + return ( + <> +
+ - - - +
+
+ +
+ + ) +} + /** * Renders a row on the mobility page. * @param {string} kind Mobility type (`bus`, `train`, `parking` or `charging`) @@ -138,17 +163,17 @@ export function RenderMobilityEntry ({ kind, item, maxLen, styles, detailed }) { const timeString = formatTimes(item.actualTime, 30, 90) return ( - <> -
- {item.name} -
-
- {item.destination.length <= maxLen ? item.destination : item.destination.substr(0, maxLen) + '…'} -
-
- {timeString} -
- + <> +
+ {item.name} +
+
+ {item.destination.length <= maxLen ? item.destination : item.destination.substr(0, maxLen) + '…'} +
+
+ {timeString} +
+ ) } else if (kind === 'parking') { return ( diff --git a/rogue-thi-app/package-lock.json b/rogue-thi-app/package-lock.json index 9d9e23fe..0cbf53ee 100644 --- a/rogue-thi-app/package-lock.json +++ b/rogue-thi-app/package-lock.json @@ -1263,9 +1263,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001446", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz", - "integrity": "sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==", + "version": "1.0.30001543", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001543.tgz", + "integrity": "sha512-qxdO8KPWPQ+Zk6bvNpPeQIOH47qZSYdFZd6dXQzb2KzhnSXju4Kd7H1PkSJx6NICSMgo/IhRZRhhfPTHYpJUCA==", "funding": [ { "type": "opencollective", @@ -1274,6 +1274,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, diff --git a/rogue-thi-app/pages/personal.jsx b/rogue-thi-app/pages/personal.jsx index 60df10a6..3667fe0d 100644 --- a/rogue-thi-app/pages/personal.jsx +++ b/rogue-thi-app/pages/personal.jsx @@ -37,6 +37,7 @@ import themes from '../data/themes.json' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { useTranslation } from 'next-i18next' +import { TextBlock } from 'react-placeholder/lib/placeholders' const PRIVACY_URL = process.env.NEXT_PUBLIC_PRIVACY_URL @@ -121,11 +122,25 @@ export default function Personal () { } }, [router, userKind]) + const placeholder = ( + <> + + + + + + + + + + + ) + return ( - + {userKind === USER_STUDENT && diff --git a/rogue-thi-app/styles/Home.module.css b/rogue-thi-app/styles/Home.module.css index 7ec027d9..f84074b0 100644 --- a/rogue-thi-app/styles/Home.module.css +++ b/rogue-thi-app/styles/Home.module.css @@ -98,3 +98,15 @@ white-space: nowrap; width: auto; } + +.placeholder_2_5 { + margin: 2.5px 0; +} + +.placeholder_3_0 { + margin: 3px 0; +} + +.placeholder_4_0 { + margin: 4px 0; +} diff --git a/rogue-thi-app/styles/Personal.module.css b/rogue-thi-app/styles/Personal.module.css index 0559db34..356e8fe2 100644 --- a/rogue-thi-app/styles/Personal.module.css +++ b/rogue-thi-app/styles/Personal.module.css @@ -33,3 +33,7 @@ justify-content: center; align-items: center; } + +.placeholder { + margin: 3.5px 0; +}