@local-civics/hub-ui 0.1.229 → 0.1.231
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +10 -9
- package/dist/index.js +66 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +67 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import React__default, { useCallback, useState, useMemo } from 'react';
|
|
3
3
|
import 'external-svg-loader';
|
|
4
|
-
import { IconCaretDown, IconCaretUp, IconSearch, IconLayoutGrid, IconList, IconBrandInstagram, IconBrandLinkedin, IconBrandFacebook, IconCategory2, IconRoute, IconAlbum, IconSwitchHorizontal, IconLogout, IconUser, IconClipboard, IconBriefcase, IconMessageCircle, IconBell,
|
|
4
|
+
import { IconCaretDown, IconCaretUp, IconSearch, IconLayoutGrid, IconList, IconBrandInstagram, IconBrandLinkedin, IconBrandFacebook, IconCategory2, IconRoute, IconAlbum, IconSwitchHorizontal, IconLogout, IconUser, IconClipboard, IconBriefcase, IconMessageCircle, IconBell, IconDownload, IconCalendar, IconCalendarStats } from '@tabler/icons';
|
|
5
5
|
import LinkifyIt from 'linkify-it';
|
|
6
6
|
import reactStringReplace from 'react-string-replace';
|
|
7
7
|
import { Link } from 'react-router-dom';
|
|
@@ -7011,42 +7011,32 @@ const FileList = (props) => {
|
|
|
7011
7011
|
return null;
|
|
7012
7012
|
}
|
|
7013
7013
|
const columns = [
|
|
7014
|
-
"minmax(0,2fr)",
|
|
7015
|
-
!hideLesson && "minmax(0,1.3fr)",
|
|
7016
7014
|
!hideBadge && "minmax(0,1.3fr)",
|
|
7015
|
+
!hideLesson && "minmax(0,1.3fr)",
|
|
7016
|
+
"minmax(0,2fr)",
|
|
7017
7017
|
"auto"
|
|
7018
7018
|
].filter(Boolean).join(" ");
|
|
7019
7019
|
const headerCell = "border-b border-slate-100 pb-2 text-[10.5px] font-extrabold uppercase tracking-wide text-slate-400";
|
|
7020
7020
|
const dataCell = "py-3";
|
|
7021
|
-
return /* @__PURE__ */ React.createElement("div", { className: "grid items-center gap-x-3", style: { gridTemplateColumns: columns } }, /* @__PURE__ */ React.createElement("div", { className: headerCell }, "
|
|
7021
|
+
return /* @__PURE__ */ React.createElement("div", { className: "grid items-center gap-x-3", style: { gridTemplateColumns: columns } }, !hideBadge && /* @__PURE__ */ React.createElement("div", { className: headerCell }, "Badge"), !hideLesson && /* @__PURE__ */ React.createElement("div", { className: headerCell }, "Lesson"), /* @__PURE__ */ React.createElement("div", { className: headerCell }, "Question"), /* @__PURE__ */ React.createElement("div", { className: headerCell }), items.map((row, i) => {
|
|
7022
7022
|
const rowBorder = i > 0 ? "border-t border-slate-100" : "";
|
|
7023
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, { key: i },
|
|
7023
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, { key: i }, !hideBadge && (row.badgeId && onBadgeClick ? /* @__PURE__ */ React.createElement(
|
|
7024
7024
|
"button",
|
|
7025
7025
|
{
|
|
7026
7026
|
type: "button",
|
|
7027
|
-
onClick: () =>
|
|
7027
|
+
onClick: () => onBadgeClick(row.badgeId),
|
|
7028
7028
|
className: `${dataCell} ${rowBorder} truncate text-left text-[11px] font-semibold text-sky-blue-400 hover:underline`
|
|
7029
7029
|
},
|
|
7030
|
-
row.
|
|
7031
|
-
) : /* @__PURE__ */ React.createElement("div", { className: `${dataCell} ${rowBorder} truncate text-[11px] text-slate-500` }, row.
|
|
7030
|
+
row.badgeName
|
|
7031
|
+
) : /* @__PURE__ */ React.createElement("div", { className: `${dataCell} ${rowBorder} truncate text-[11px] text-slate-500` }, row.badgeName)), !hideLesson && (row.lessonId && onLessonClick ? /* @__PURE__ */ React.createElement(
|
|
7032
7032
|
"button",
|
|
7033
7033
|
{
|
|
7034
7034
|
type: "button",
|
|
7035
|
-
onClick: () =>
|
|
7035
|
+
onClick: () => onLessonClick(row.lessonId),
|
|
7036
7036
|
className: `${dataCell} ${rowBorder} truncate text-left text-[11px] font-semibold text-sky-blue-400 hover:underline`
|
|
7037
7037
|
},
|
|
7038
|
-
row.
|
|
7039
|
-
) : /* @__PURE__ */ React.createElement("div", { className: `${dataCell} ${rowBorder} truncate text-[11px] text-slate-500` }, row.
|
|
7040
|
-
"a",
|
|
7041
|
-
{
|
|
7042
|
-
href: row.link,
|
|
7043
|
-
target: "_blank",
|
|
7044
|
-
rel: "noopener noreferrer",
|
|
7045
|
-
className: "flex items-center gap-1 rounded-md border border-slate-200 px-2.5 py-1.5 text-[11px] font-bold text-slate-600 no-underline hover:bg-slate-50"
|
|
7046
|
-
},
|
|
7047
|
-
/* @__PURE__ */ React.createElement(IconExternalLink, { size: 12, stroke: 2 }),
|
|
7048
|
-
"Preview"
|
|
7049
|
-
), /* @__PURE__ */ React.createElement(
|
|
7038
|
+
row.lessonName
|
|
7039
|
+
) : /* @__PURE__ */ React.createElement("div", { className: `${dataCell} ${rowBorder} truncate text-[11px] text-slate-500` }, row.lessonName)), /* @__PURE__ */ React.createElement("div", { className: `${dataCell} ${rowBorder} text-xs font-semibold text-dark-blue-400` }, row.question), /* @__PURE__ */ React.createElement("div", { className: `${dataCell} ${rowBorder} flex shrink-0 gap-2` }, /* @__PURE__ */ React.createElement(
|
|
7050
7040
|
"a",
|
|
7051
7041
|
{
|
|
7052
7042
|
href: row.link,
|
|
@@ -7076,9 +7066,18 @@ const TABS = [
|
|
|
7076
7066
|
{ value: "badges", label: "By badge" },
|
|
7077
7067
|
{ value: "lessons", label: "By lesson" }
|
|
7078
7068
|
];
|
|
7069
|
+
const SEARCH_PLACEHOLDER = "Search by pathway, badge, lesson, or question";
|
|
7079
7070
|
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
7071
|
+
const matchesSearch = (item, term) => {
|
|
7072
|
+
var _a;
|
|
7073
|
+
if (!term)
|
|
7074
|
+
return true;
|
|
7075
|
+
return item.badgeName.toLowerCase().includes(term) || item.lessonName.toLowerCase().includes(term) || !!((_a = item.pathwayName) == null ? void 0 : _a.toLowerCase().includes(term)) || item.question.toLowerCase().includes(term);
|
|
7076
|
+
};
|
|
7080
7077
|
const FileLocker = (props) => {
|
|
7081
7078
|
const [tab, setTab] = React.useState("pathways");
|
|
7079
|
+
const [search, setSearch] = React.useState("");
|
|
7080
|
+
const searchLower = search.trim().toLowerCase();
|
|
7082
7081
|
const filtered = useFilteredSubmissions(props.submissions);
|
|
7083
7082
|
const files = props.submissions.length;
|
|
7084
7083
|
const now = Date.now();
|
|
@@ -7086,9 +7085,24 @@ const FileLocker = (props) => {
|
|
|
7086
7085
|
const startOfMonth = new Date(new Date().getFullYear(), new Date().getMonth(), 1).getTime();
|
|
7087
7086
|
const thisWeek = props.submissions.filter((s) => s.updatedAt && new Date(s.updatedAt).getTime() >= weekAgo).length;
|
|
7088
7087
|
const thisMonth = props.submissions.filter((s) => s.updatedAt && new Date(s.updatedAt).getTime() >= startOfMonth).length;
|
|
7089
|
-
const pathwayGroups = props.pathways.map((p) =>
|
|
7090
|
-
|
|
7091
|
-
|
|
7088
|
+
const pathwayGroups = props.pathways.map((p) => {
|
|
7089
|
+
const base = filtered.byPathway(p.pathwayId, props.badges);
|
|
7090
|
+
const ownNameMatches = !searchLower || p.title.toLowerCase().includes(searchLower);
|
|
7091
|
+
const items = ownNameMatches ? base : base.filter((it) => matchesSearch(it, searchLower));
|
|
7092
|
+
return { pathway: p, items };
|
|
7093
|
+
}).filter((g) => g.items.length > 0);
|
|
7094
|
+
const badgeGroups = props.badges.map((b) => {
|
|
7095
|
+
const base = filtered.byBadge(b.badgeId);
|
|
7096
|
+
const ownNameMatches = !searchLower || b.displayName.toLowerCase().includes(searchLower);
|
|
7097
|
+
const items = ownNameMatches ? base : base.filter((it) => matchesSearch(it, searchLower));
|
|
7098
|
+
return { badge: b, items };
|
|
7099
|
+
}).filter((g) => g.items.length > 0);
|
|
7100
|
+
const lessonGroups = props.lessons.map((l) => {
|
|
7101
|
+
const base = filtered.byLesson(l.lessonId);
|
|
7102
|
+
const ownNameMatches = !searchLower || l.lessonName.toLowerCase().includes(searchLower);
|
|
7103
|
+
const items = ownNameMatches ? base : base.filter((it) => matchesSearch(it, searchLower));
|
|
7104
|
+
return { lesson: l, items };
|
|
7105
|
+
}).filter((g) => g.items.length > 0);
|
|
7092
7106
|
return /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-5" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h1", { className: "text-2xl font-extrabold tracking-tight text-dark-blue-400" }, props.displayName || "File Locker"), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-500" }, props.description || "Files and links you've submitted through your lessons")), /* @__PURE__ */ React.createElement("div", { className: "flex gap-2.5" }, /* @__PURE__ */ React.createElement(StatCell, { icon: IconClipboard, value: files, label: "Files", accent: "cyan" }), /* @__PURE__ */ React.createElement(StatCell, { icon: IconCalendar, value: thisWeek, label: "This Week", accent: "mint" }), /* @__PURE__ */ React.createElement(StatCell, { icon: IconCalendarStats, value: thisMonth, label: "This Month", accent: "gold" })), /* @__PURE__ */ React.createElement("div", { className: "flex w-fit gap-1 rounded-xl border border-slate-200 bg-white p-1" }, TABS.map((t) => /* @__PURE__ */ React.createElement(
|
|
7093
7107
|
"button",
|
|
7094
7108
|
{
|
|
@@ -7097,7 +7111,16 @@ const FileLocker = (props) => {
|
|
|
7097
7111
|
className: `rounded-lg px-4 py-2 text-xs font-bold ${tab === t.value ? "bg-sky-blue-400/20 text-dark-blue-400" : "text-slate-400 hover:text-slate-600"}`
|
|
7098
7112
|
},
|
|
7099
7113
|
t.label
|
|
7100
|
-
))), /* @__PURE__ */ React.createElement("div", { className: "flex
|
|
7114
|
+
))), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2.5 rounded-xl border border-slate-200 bg-white px-4 py-3 shadow-sm" }, /* @__PURE__ */ React.createElement(IconSearch, { size: 15, stroke: 1.75, className: "text-slate-400" }), /* @__PURE__ */ React.createElement(
|
|
7115
|
+
"input",
|
|
7116
|
+
{
|
|
7117
|
+
type: "text",
|
|
7118
|
+
value: search,
|
|
7119
|
+
onChange: (e) => setSearch(e.target.value),
|
|
7120
|
+
placeholder: SEARCH_PLACEHOLDER,
|
|
7121
|
+
className: "flex-1 border-none bg-transparent text-sm text-dark-blue-400 outline-none placeholder:text-slate-400"
|
|
7122
|
+
}
|
|
7123
|
+
)), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-3" }, props.loading && /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "Loading..."), !props.loading && tab === "pathways" && pathwayGroups.map(({ pathway, items }) => /* @__PURE__ */ React.createElement(
|
|
7101
7124
|
GroupCard,
|
|
7102
7125
|
{
|
|
7103
7126
|
key: pathway.pathwayId,
|
|
@@ -7106,7 +7129,25 @@ const FileLocker = (props) => {
|
|
|
7106
7129
|
onClick: props.onPathwayClick ? () => props.onPathwayClick(pathway.pathwayId) : void 0
|
|
7107
7130
|
},
|
|
7108
7131
|
/* @__PURE__ */ React.createElement(FileList, { items, onBadgeClick: props.onBadgeClick, onLessonClick: props.onLessonClick })
|
|
7109
|
-
)), !props.loading && tab === "badges" && badgeGroups.map(({ badge, items }) => /* @__PURE__ */ React.createElement(
|
|
7132
|
+
)), !props.loading && tab === "badges" && badgeGroups.map(({ badge, items }) => /* @__PURE__ */ React.createElement(
|
|
7133
|
+
GroupCard,
|
|
7134
|
+
{
|
|
7135
|
+
key: badge.badgeId,
|
|
7136
|
+
title: badge.displayName,
|
|
7137
|
+
description: badgeSubtitle(items),
|
|
7138
|
+
onClick: props.onBadgeClick ? () => props.onBadgeClick(badge.badgeId) : void 0
|
|
7139
|
+
},
|
|
7140
|
+
/* @__PURE__ */ React.createElement(FileList, { items, hideBadge: true, onLessonClick: props.onLessonClick })
|
|
7141
|
+
)), !props.loading && tab === "lessons" && lessonGroups.map(({ lesson, items }) => /* @__PURE__ */ React.createElement(
|
|
7142
|
+
GroupCard,
|
|
7143
|
+
{
|
|
7144
|
+
key: lesson.lessonId,
|
|
7145
|
+
title: lesson.lessonName,
|
|
7146
|
+
description: lessonSubtitle(items),
|
|
7147
|
+
onClick: props.onLessonClick ? () => props.onLessonClick(lesson.lessonId) : void 0
|
|
7148
|
+
},
|
|
7149
|
+
/* @__PURE__ */ React.createElement(FileList, { items, hideBadge: true, hideLesson: true })
|
|
7150
|
+
)), !props.loading && files > 0 && searchLower && (tab === "pathways" && pathwayGroups.length === 0 || tab === "badges" && badgeGroups.length === 0 || tab === "lessons" && lessonGroups.length === 0) && /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "No matching entries."), !props.loading && files === 0 && /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "No files to display.")));
|
|
7110
7151
|
};
|
|
7111
7152
|
const badgeSubtitle = (items) => {
|
|
7112
7153
|
var _a;
|