diff --git a/Dockerfile b/Dockerfile index 67bfdc1..e940d6c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -82,8 +82,11 @@ COPY --from=builder /build/client/dist ./public # Zkopírování produkčních .env serveru COPY /server/.env.production ./server -# Zkopírování konfigurace easter eggů -RUN if [ -f /server/.easter-eggs.json ]; then cp /server/.easter-eggs.json ./server/; fi +# Zkopírování changelogů (seznamu novinek) +COPY /server/changelogs ./server/changelogs + +# Zkopírování konfigurace easter eggů a changelogů +RUN if [ -f ./server/.easter-eggs.json ]; then cp ./server/.easter-eggs.json ./server/; fi # Export /data/db.json do složky /data VOLUME ["/data"] diff --git a/Dockerfile-Woodpecker b/Dockerfile-Woodpecker index 6291b32..3dc9ec2 100644 --- a/Dockerfile-Woodpecker +++ b/Dockerfile-Woodpecker @@ -18,8 +18,12 @@ COPY ./server/dist ./ # Vykopírování sestaveného klienta COPY ./client/dist ./public -# Zkopírování konfigurace easter eggů -RUN if [ -f ./server/.easter-eggs.json ]; then cp ./server/.easter-eggs.json ./server/; fi +# Zkopírování changelogů (seznamu novinek) +COPY ./server/changelogs ./server/changelogs + +# Zkopírování konfigurace easter eggů a changelogů +RUN if [ -f ./server/.easter-eggs.json ]; then cp ./server/.easter-eggs.json ./server/; fi \ + && if [ -d ./server/changelogs ]; then cp -r ./server/changelogs ./server/changelogs; fi EXPOSE 3000 diff --git a/client/src/components/Header.tsx b/client/src/components/Header.tsx index 0af7f47..9b279c6 100644 --- a/client/src/components/Header.tsx +++ b/client/src/components/Header.tsx @@ -11,16 +11,12 @@ import GenerateMockDataModal from "./modals/GenerateMockDataModal"; import ClearMockDataModal from "./modals/ClearMockDataModal"; import { useNavigate } from "react-router"; import { STATS_URL } from "../AppRoutes"; -import { FeatureRequest, getVotes, updateVote, LunchChoices } from "../../../types"; +import { FeatureRequest, getVotes, updateVote, LunchChoices, getChangelogs } from "../../../types"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faSun, faMoon } from "@fortawesome/free-solid-svg-icons"; +import { formatDateString } from "../Utils"; -const CHANGELOG = [ - "Nový moderní design aplikace", - "Oprava parsování Sladovnické a TechTower", - "Možnost označit se jako objednávající u volby \"budu objednávat\"", - "Možnost generovat QR kódy pro platby (i mimo Pizza day)", -]; +const LAST_SEEN_CHANGELOG_KEY = "lastChangelogDate"; const IS_DEV = process.env.NODE_ENV === 'development'; @@ -38,6 +34,7 @@ export default function Header({ choices, dayIndex }: Props) { const [pizzaModalOpen, setPizzaModalOpen] = useState(false); const [refreshMenuModalOpen, setRefreshMenuModalOpen] = useState(false); const [changelogModalOpen, setChangelogModalOpen] = useState(false); + const [changelogEntries, setChangelogEntries] = useState>({}); const [qrModalOpen, setQrModalOpen] = useState(false); const [generateMockModalOpen, setGenerateMockModalOpen] = useState(false); const [clearMockModalOpen, setClearMockModalOpen] = useState(false); @@ -71,6 +68,19 @@ export default function Header({ choices, dayIndex }: Props) { } }, [auth?.login]); + useEffect(() => { + if (!auth?.login) return; + const lastSeen = localStorage.getItem(LAST_SEEN_CHANGELOG_KEY) ?? undefined; + getChangelogs({ query: lastSeen ? { since: lastSeen } : {} }).then(response => { + const entries = response.data; + if (!entries || Object.keys(entries).length === 0) return; + setChangelogEntries(entries); + setChangelogModalOpen(true); + const newestDate = Object.keys(entries).sort((a, b) => b.localeCompare(a))[0]; + localStorage.setItem(LAST_SEEN_CHANGELOG_KEY, newestDate); + }); + }, [auth?.login]); + const closeSettingsModal = () => { setSettingsModalOpen(false); } @@ -197,7 +207,17 @@ export default function Header({ choices, dayIndex }: Props) { setPizzaModalOpen(true)}>Pizza kalkulačka Generování QR kódů navigate(STATS_URL)}>Statistiky - setChangelogModalOpen(true)}>Novinky + { + getChangelogs().then(response => { + const entries = response.data ?? {}; + setChangelogEntries(entries); + setChangelogModalOpen(true); + const dates = Object.keys(entries).sort((a, b) => b.localeCompare(a)); + if (dates.length > 0) { + localStorage.setItem(LAST_SEEN_CHANGELOG_KEY, dates[0]); + } + }); + }}>Novinky {IS_DEV && ( <> @@ -237,16 +257,24 @@ export default function Header({ choices, dayIndex }: Props) { /> )} - setChangelogModalOpen(false)}> + setChangelogModalOpen(false)} size="lg">

Novinky

-
    - {CHANGELOG.map((item, index) => ( -
  • {item}
  • - ))} -
+ {Object.keys(changelogEntries).sort((a, b) => b.localeCompare(a)).map(date => ( +
+ {formatDateString(date)} +
    + {changelogEntries[date].map((item, index) => ( +
  • {item}
  • + ))} +
+
+ ))} + {Object.keys(changelogEntries).length === 0 && ( +

Žádné novinky.

+ )}