Adds functionality to load server-side checklist, then update locally later
parent
f4bb43ab40
commit
9439d7f490
|
@ -17,6 +17,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@builder.io/qwik-city": "^1.1.4",
|
"@builder.io/qwik-city": "^1.1.4",
|
||||||
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/node": "^20.11.16",
|
"@types/node": "^20.11.16",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.20.0",
|
"@typescript-eslint/eslint-plugin": "^6.20.0",
|
||||||
"@typescript-eslint/parser": "^6.20.0",
|
"@typescript-eslint/parser": "^6.20.0",
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/web-manifest-combined.json",
|
"$schema": "https://json.schemastore.org/web-manifest-combined.json",
|
||||||
"name": "qwik-project-name",
|
"name": "digital-defense",
|
||||||
"short_name": "Welcome to Qwik",
|
"short_name": "Digital Defense",
|
||||||
"start_url": ".",
|
"start_url": ".",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#fff",
|
"background_color": "#5616c6",
|
||||||
"description": "A Qwik project app."
|
"description": "The ultimate personal security checklist"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// src/routes/_404.tsx
|
||||||
|
import { component$ } from '@builder.io/qwik';
|
||||||
|
|
||||||
|
export default component$(() => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>404 Not Found</h1>
|
||||||
|
<p>The page you're looking for doesn't exist.</p>
|
||||||
|
<a href="/">Go back to the homepage</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
|
@ -212,11 +212,11 @@ export default component$(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
export const head: DocumentHead = {
|
export const head: DocumentHead = {
|
||||||
title: "Welcome to Qwik",
|
title: "About | Digital Defense",
|
||||||
meta: [
|
meta: [
|
||||||
{
|
{
|
||||||
name: "description",
|
name: "description",
|
||||||
content: "Qwik site description",
|
content: "This project aims to give you practical guidance on how to improve your digital security, and protect your privacy online",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,17 +4,20 @@ import { marked } from 'marked';
|
||||||
|
|
||||||
import Icon from '~/components/core/icon';
|
import Icon from '~/components/core/icon';
|
||||||
import { ChecklistContext } from '~/store/checklist-context';
|
import { ChecklistContext } from '~/store/checklist-context';
|
||||||
|
import { useChecklist } from '~/store/local-checklist-store';
|
||||||
import type { Section } from "~/types/PSC";
|
import type { Section } from "~/types/PSC";
|
||||||
import Table from '~/components/psc/checklist-table';
|
import Table from '~/components/psc/checklist-table';
|
||||||
|
|
||||||
export default component$(() => {
|
export default component$(() => {
|
||||||
|
|
||||||
const checklists = useContext(ChecklistContext);
|
const checklists = useContext(ChecklistContext);
|
||||||
|
const localChecklist = useChecklist();
|
||||||
|
|
||||||
const loc = useLocation();
|
const loc = useLocation();
|
||||||
const slug = loc.params.title;
|
const slug = loc.params.title;
|
||||||
|
|
||||||
const section: Section | undefined = checklists.value.find((item: Section) => item.slug === slug);
|
const section: Section | undefined = (localChecklist.checklist.checklist || checklists.value)
|
||||||
|
.find((item: Section) => item.slug === slug);
|
||||||
|
|
||||||
const parseMarkdown = (text: string | undefined): string => {
|
const parseMarkdown = (text: string | undefined): string => {
|
||||||
return marked.parse(text || '', { async: false }) as string || '';
|
return marked.parse(text || '', { async: false }) as string || '';
|
||||||
|
|
|
@ -2,17 +2,19 @@ import { component$, useContext } from "@builder.io/qwik";
|
||||||
|
|
||||||
import { ChecklistContext } from '~/store/checklist-context';
|
import { ChecklistContext } from '~/store/checklist-context';
|
||||||
import { useLocalStorage } from "~/hooks/useLocalStorage";
|
import { useLocalStorage } from "~/hooks/useLocalStorage";
|
||||||
|
import { useChecklist } from '~/store/local-checklist-store';
|
||||||
import type { Section } from "~/types/PSC";
|
import type { Section } from "~/types/PSC";
|
||||||
|
|
||||||
export default component$(() => {
|
export default component$(() => {
|
||||||
const checklists = useContext(ChecklistContext);
|
const checklists = useContext(ChecklistContext);
|
||||||
|
const localChecklist = useChecklist();
|
||||||
|
|
||||||
const [completed, setCompleted] = useLocalStorage('PSC_PROGRESS', {});
|
const [completed, setCompleted] = useLocalStorage('PSC_PROGRESS', {});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main class="p-8">
|
<main class="p-8">
|
||||||
<div class="join join-vertical w-full">
|
<div class="join join-vertical w-full">
|
||||||
{checklists.value.map((section: Section, index: number) => (
|
{(localChecklist.checklist.checklist || checklists.value).map((section: Section, index: number) => (
|
||||||
<div key={index} class={['collapse collapse-plus bg-base-200 my-4', `border-double border-2 border-${section.color}-400`]}>
|
<div key={index} class={['collapse collapse-plus bg-base-200 my-4', `border-double border-2 border-${section.color}-400`]}>
|
||||||
<input type="radio" name="my-accordion-3" />
|
<input type="radio" name="my-accordion-3" />
|
||||||
<div class={['collapse-title text-xl font-medium']}>
|
<div class={['collapse-title text-xl font-medium']}>
|
||||||
|
|
|
@ -7,14 +7,18 @@ import Progress from "~/components/psc/progress";
|
||||||
|
|
||||||
import { ChecklistContext } from '~/store/checklist-context';
|
import { ChecklistContext } from '~/store/checklist-context';
|
||||||
|
|
||||||
|
import { useChecklist } from '~/store/local-checklist-store';
|
||||||
|
|
||||||
export default component$(() => {
|
export default component$(() => {
|
||||||
const checklists = useContext(ChecklistContext);
|
const checklists = useContext(ChecklistContext);
|
||||||
|
|
||||||
|
const localChecklist = useChecklist();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Hero />
|
<Hero />
|
||||||
<Progress />
|
<Progress />
|
||||||
<SectionLinkGrid sections={checklists.value} />
|
<SectionLinkGrid sections={localChecklist.checklist.checklist || checklists.value} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
import { component$, useContextProvider, Slot } from "@builder.io/qwik";
|
import { component$, useContextProvider, Slot } from "@builder.io/qwik";
|
||||||
import { routeLoader$, type RequestHandler } from "@builder.io/qwik-city";
|
import { routeLoader$, type RequestHandler } from "@builder.io/qwik-city";
|
||||||
|
|
||||||
import Navbar from "../components/furniture/nav";
|
|
||||||
import Footer from "../components/furniture/footer";
|
|
||||||
|
|
||||||
import { ChecklistContext } from "../store/checklist-context";
|
|
||||||
|
|
||||||
import { type Sections } from "~/types/PSC";
|
|
||||||
import jsyaml from "js-yaml";
|
import jsyaml from "js-yaml";
|
||||||
|
|
||||||
|
import Navbar from "~/components/furniture/nav";
|
||||||
|
import Footer from "~/components/furniture/footer";
|
||||||
|
import { ChecklistContext } from "~/store/checklist-context";
|
||||||
|
import type { Sections } from "~/types/PSC";
|
||||||
|
|
||||||
export const useChecklists = routeLoader$(async () => {
|
export const useChecklists = routeLoader$(async () => {
|
||||||
const url = import.meta.env.DEV ? `http://localhost:5173/personal-security-checklist.yml` : '/personal-security-checklist.yml';
|
const remoteUrl = 'https://gist.githubusercontent.com/Lissy93/0c26e4255b6fabc2c027ac72a4428aeb/raw/4ccdbc71e0fffdef53472cf98acbe40b0acf982b/personal-security-checklist.yml';
|
||||||
return await fetch(url)
|
// TODO: Update this URL to point to the Git repository
|
||||||
|
return fetch(remoteUrl)
|
||||||
.then((res) => res.text())
|
.then((res) => res.text())
|
||||||
.then((res) => jsyaml.load(res) as Sections)
|
.then((res) => jsyaml.load(res) as Sections)
|
||||||
.catch(() => []);
|
.catch(() => []);
|
||||||
|
@ -26,13 +24,11 @@ export const onGet: RequestHandler = async ({ cacheControl }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default component$(() => {
|
export default component$(() => {
|
||||||
|
|
||||||
const checklists = useChecklists();
|
const checklists = useChecklists();
|
||||||
useContextProvider(ChecklistContext, checklists)
|
useContextProvider(ChecklistContext, checklists);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* <Header /> */}
|
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<main class="bg-base-100 min-h-full">
|
<main class="bg-base-100 min-h-full">
|
||||||
<Slot />
|
<Slot />
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
import { $, useStore, useOnWindow } from '@builder.io/qwik';
|
||||||
|
import jsyaml from 'js-yaml';
|
||||||
|
import type { Sections } from '~/types/PSC';
|
||||||
|
|
||||||
|
export const useChecklist = () => {
|
||||||
|
const state = useStore<{ checklist: Sections | null }>({ checklist: null });
|
||||||
|
|
||||||
|
const fetchChecklist = $(async () => {
|
||||||
|
const localUrl = '/personal-security-checklist.yml';
|
||||||
|
return fetch(localUrl)
|
||||||
|
.then((res) => res.text())
|
||||||
|
.then((yamlText) => {
|
||||||
|
return jsyaml.load(yamlText);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
useOnWindow('load', $(() => {
|
||||||
|
fetchChecklist().then((checklist) => {
|
||||||
|
state.checklist = checklist as Sections;
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
const setChecklist = $((newChecklist: Sections) => {
|
||||||
|
state.checklist = newChecklist;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { checklist: state, setChecklist };
|
||||||
|
};
|
|
@ -656,6 +656,11 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/unist" "^2"
|
"@types/unist" "^2"
|
||||||
|
|
||||||
|
"@types/js-yaml@^4.0.9":
|
||||||
|
version "4.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2"
|
||||||
|
integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==
|
||||||
|
|
||||||
"@types/json-schema@^7.0.12":
|
"@types/json-schema@^7.0.12":
|
||||||
version "7.0.15"
|
version "7.0.15"
|
||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||||
|
|
Loading…
Reference in New Issue