diff --git a/public/icons/file-plus.svg b/public/icons/file-plus.svg
new file mode 100644
index 0000000..c9a0fdb
--- /dev/null
+++ b/public/icons/file-plus.svg
@@ -0,0 +1,9 @@
+
+
+
diff --git a/public/icons/list.svg b/public/icons/list.svg
new file mode 100644
index 0000000..9da4f57
--- /dev/null
+++ b/public/icons/list.svg
@@ -0,0 +1,11 @@
+
+
+
diff --git a/src/components/Page/Page.tsx b/src/components/Page/Page.tsx
index a0c01f6..37c7257 100644
--- a/src/components/Page/Page.tsx
+++ b/src/components/Page/Page.tsx
@@ -6,7 +6,10 @@ import {
ExpressionSetListView,
} from "../../views";
import { AddExpressionView } from "../../views/AddExpressionView";
+import { ExpressionCardListView } from "../../views/ExpressionCardListView";
+import { ExpressionCardView } from "../../views/ExpressionCardView";
import { HomeView } from "../../views/HomeView";
+import { SettingsView } from "../../views/SettingsView";
export function Page() {
const { route } = useContext(AppRouting);
@@ -22,7 +25,12 @@ export function Page() {
case AppPath.ExpressionSetsPractice:
return ;
case AppPath.Settings:
- // TODO this should split onto more views
+ return ;
+ case AppPath.CreateCards:
return ;
+ case AppPath.CardsList:
+ return ;
+ case AppPath.CardView:
+ return ;
}
}
diff --git a/src/components/SettingsSection/SettingsSectionLink.tsx b/src/components/SettingsSection/SettingsSectionLink.tsx
new file mode 100644
index 0000000..9bff87d
--- /dev/null
+++ b/src/components/SettingsSection/SettingsSectionLink.tsx
@@ -0,0 +1,26 @@
+import { useContext } from "react";
+import { AppPath, AppRouting } from "../../model/routing";
+
+export interface SettingsSectionLinkProps {
+ text: string;
+ iconUrl?: string;
+ page: AppPath;
+}
+
+export function SettingsSectionLink({
+ text,
+ iconUrl,
+ page,
+}: SettingsSectionLinkProps) {
+ const { setRoute } = useContext(AppRouting);
+ return (
+
setRoute({ path: page })}>
+
+
+ {iconUrl &&

}
+
{text}
+
+
+
+ );
+}
diff --git a/src/components/SettingsSection/index.ts b/src/components/SettingsSection/index.ts
new file mode 100644
index 0000000..045c9f8
--- /dev/null
+++ b/src/components/SettingsSection/index.ts
@@ -0,0 +1 @@
+export * from "./SettingsSectionLink";
diff --git a/src/hooks/index.ts b/src/hooks/index.ts
index 2158ec8..ba7648a 100644
--- a/src/hooks/index.ts
+++ b/src/hooks/index.ts
@@ -1,5 +1,8 @@
export * from "./useExpressionCategories";
+export * from "./useExpressionById";
export * from "./useExpressionFilterQueryIds";
+export * from "./useExpressionQueryId";
+export * from "./useExpressions";
export * from "./useExpressionSet";
export * from "./useExpressionSetQueryId";
export * from "./useExpressionSets";
diff --git a/src/hooks/useExpressionById.ts b/src/hooks/useExpressionById.ts
new file mode 100644
index 0000000..3af7ccc
--- /dev/null
+++ b/src/hooks/useExpressionById.ts
@@ -0,0 +1,9 @@
+import { useLiveQuery } from "dexie-react-hooks";
+import { database, IndexedExpression } from "../model";
+
+export function useExpressionById(id?: number): IndexedExpression | undefined {
+ return useLiveQuery(() => {
+ if (id === undefined) return undefined;
+ return database.expressions.where({ id }).first();
+ }, [id]);
+}
diff --git a/src/hooks/useExpressionQueryId.ts b/src/hooks/useExpressionQueryId.ts
new file mode 100644
index 0000000..2476b20
--- /dev/null
+++ b/src/hooks/useExpressionQueryId.ts
@@ -0,0 +1,7 @@
+import { useContext } from "react";
+import { AppRouting } from "../model/routing";
+
+export function useExpressionQueryId(): number | undefined {
+ const { route } = useContext(AppRouting);
+ return route.options?.expression_card_id;
+}
diff --git a/src/hooks/useExpressions.ts b/src/hooks/useExpressions.ts
new file mode 100644
index 0000000..162dfea
--- /dev/null
+++ b/src/hooks/useExpressions.ts
@@ -0,0 +1,9 @@
+import { useLiveQuery } from "dexie-react-hooks";
+import { database } from "../model";
+
+export function useExpressions() {
+ return useLiveQuery(
+ () => database.expressions.toArray(),
+ [database.expressions]
+ );
+}
diff --git a/src/model/routing.ts b/src/model/routing.ts
index 0fd872c..adef827 100644
--- a/src/model/routing.ts
+++ b/src/model/routing.ts
@@ -7,10 +7,16 @@ export enum AppPath {
ExpressionSetsPractice = "expression-sets/practice",
ExpressionSetsDetails = "expression-sets/details",
Settings = "settings",
- CreateCards = "settings", // TODO split from settings
+ CardsList = "settings/cards",
+ CardView = "settings/view-card",
+ CreateCards = "settings/create-cards",
}
export interface RouteOptions {
+ // Used in cards view
+ expression_card_id?: number;
+
+ // Used in practice view
expression_set_id?: number;
expression_id_filters?: number[];
}
diff --git a/src/views/ExpressionCardListView/ExpressionCardListView.tsx b/src/views/ExpressionCardListView/ExpressionCardListView.tsx
new file mode 100644
index 0000000..ba5324e
--- /dev/null
+++ b/src/views/ExpressionCardListView/ExpressionCardListView.tsx
@@ -0,0 +1,50 @@
+import { useContext, useMemo } from "react";
+import { ExpressionCard } from "../../components";
+import { useExpressions } from "../../hooks";
+import { IndexedExpression } from "../../model";
+import { AppPath, AppRouting } from "../../model/routing";
+import { ErrorView } from "../ErrorView";
+
+export function ExpressionCardListView() {
+ const { setRoute } = useContext(AppRouting);
+ const expressions = useExpressions();
+ const expression_list = useMemo(
+ () => (expressions || []).concat().sort(sort_function),
+ [expressions]
+ );
+
+ if (!expressions) return null; // LOADING
+ if (!expressions.length) {
+ return ;
+ }
+
+ return (
+
+
+ {expression_list.map(({ prompt, id, description }) => (
+ -
+ setRoute({
+ path: AppPath.CardView,
+ options: { expression_card_id: id },
+ })
+ }
+ >
+
+
+ ))}
+
+
+ );
+}
+
+function sort_function(a: IndexedExpression, b: IndexedExpression) {
+ if (a.prompt < b.prompt) return -1;
+ if (a.prompt > b.prompt) return 1;
+ return 0;
+}
diff --git a/src/views/ExpressionCardListView/index.ts b/src/views/ExpressionCardListView/index.ts
new file mode 100644
index 0000000..a06c500
--- /dev/null
+++ b/src/views/ExpressionCardListView/index.ts
@@ -0,0 +1 @@
+export * from "./ExpressionCardListView";
diff --git a/src/views/ExpressionCardView/ExpressionCardView.tsx b/src/views/ExpressionCardView/ExpressionCardView.tsx
new file mode 100644
index 0000000..e250b99
--- /dev/null
+++ b/src/views/ExpressionCardView/ExpressionCardView.tsx
@@ -0,0 +1,20 @@
+import { ExpressionCard } from "../../components";
+import { ExpressionDescription } from "../../components/ExpressionDescription";
+import { useExpressionById, useExpressionQueryId } from "../../hooks";
+import { ErrorView } from "../ErrorView";
+
+export function ExpressionCardView() {
+ const expression_id = useExpressionQueryId();
+ const expression = useExpressionById(expression_id);
+ if (!expression) return ;
+ return (
+
+ }
+ show_description
+ />
+
+ );
+}
diff --git a/src/views/ExpressionCardView/index.ts b/src/views/ExpressionCardView/index.ts
new file mode 100644
index 0000000..afd407e
--- /dev/null
+++ b/src/views/ExpressionCardView/index.ts
@@ -0,0 +1 @@
+export * from "./ExpressionCardView";
diff --git a/src/views/SettingsView/SettingsView.tsx b/src/views/SettingsView/SettingsView.tsx
new file mode 100644
index 0000000..40e3afb
--- /dev/null
+++ b/src/views/SettingsView/SettingsView.tsx
@@ -0,0 +1,21 @@
+import { SettingsSectionLink } from "../../components/SettingsSection";
+import { AppPath } from "../../model/routing";
+
+export function SettingsView() {
+ return (
+
+ );
+}
diff --git a/src/views/SettingsView/index.ts b/src/views/SettingsView/index.ts
new file mode 100644
index 0000000..eb34623
--- /dev/null
+++ b/src/views/SettingsView/index.ts
@@ -0,0 +1 @@
+export * from "./SettingsView";