@variamosple/variamos-components 0.2.3 → 0.2.5

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.
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The address without its authToken parameter, or null when it has none.
3
+ *
4
+ * The token is never read from an address: a link could otherwise force a
5
+ * session chosen by someone else (session fixation). The session lives in
6
+ * the httpOnly cookie set by the admin; a token left by an older link is
7
+ * only removed from the address bar and the history.
8
+ */
9
+ export declare const addressWithoutToken: (href: string) => string | null;
@@ -0,0 +1,7 @@
1
+ const t = (r) => {
2
+ const e = new URL(r);
3
+ return e.searchParams.has("authToken") ? (e.searchParams.delete("authToken"), e.toString()) : null;
4
+ };
5
+ export {
6
+ t as addressWithoutToken
7
+ };
@@ -1,36 +1,37 @@
1
- import { jsx as o, Fragment as u } from "react/jsx-runtime";
2
- import d, { useEffect as m } from "react";
3
- import { Spinner as f } from "react-bootstrap";
1
+ import { jsx as o, Fragment as f } from "react/jsx-runtime";
2
+ import p, { useEffect as m } from "react";
3
+ import { Spinner as h } from "react-bootstrap";
4
4
  import "../../Context/AnalyticsContext.js";
5
5
  import "../../Context/MenuContext.js";
6
- import { useRouter as p } from "../../Context/RouterContext.js";
7
- import { useSession as l } from "../../Context/SessionContext.js";
6
+ import { useRouter as u } from "../../Context/RouterContext.js";
7
+ import { useSession as g } from "../../Context/SessionContext.js";
8
8
  import "../../Context/NotificationContext.js";
9
- const v = ({
9
+ import { addressWithoutToken as l } from "./addressWithoutToken.js";
10
+ const j = ({
10
11
  redirectPath: i,
11
12
  children: c
12
13
  }) => {
13
- const [a, h] = d.useState(!1), { isAuthenticated: t, isLoading: r } = l(), { navigate: n } = p();
14
+ const [n, d] = p.useState(!1), { isAuthenticated: t, isLoading: r } = g(), { navigate: s } = u();
14
15
  return m(() => {
15
- if (!r && !t && !a) {
16
- const e = window.location.href, s = new URLSearchParams();
17
- s.set("redirectTo", encodeURIComponent(e)), h(!0), setTimeout(
18
- () => n(`${i}?${s.toString()}`),
16
+ if (!r && !t && !n) {
17
+ const e = window.location.href, a = new URLSearchParams();
18
+ a.set("redirectTo", encodeURIComponent(e)), d(!0), setTimeout(
19
+ () => s(`${i}?${a.toString()}`),
19
20
  300
20
21
  );
21
22
  }
22
- }, [t, r, i, n, a]), m(() => {
23
- const e = new URL(window.location.href);
24
- e.searchParams.has("authToken") && (localStorage.setItem("authToken", e.searchParams.get("authToken")), e.searchParams.delete("authToken"), window.history.replaceState({}, "", e));
23
+ }, [t, r, i, s, n]), m(() => {
24
+ const e = l(window.location.href);
25
+ e && window.history.replaceState({}, "", e);
25
26
  }, []), r || !t ? /* @__PURE__ */ o("div", { className: "d-flex justify-content-center align-items-center w-100 h-100", children: /* @__PURE__ */ o(
26
- f,
27
+ h,
27
28
  {
28
29
  animation: "border",
29
30
  variant: "primary",
30
31
  style: { width: "3rem", height: "3rem", borderWidth: "0.5rem" }
31
32
  }
32
- ) }) : /* @__PURE__ */ o(u, { children: c });
33
+ ) }) : /* @__PURE__ */ o(f, { children: c });
33
34
  };
34
35
  export {
35
- v as AuthWrapper
36
+ j as AuthWrapper
36
37
  };
@@ -1,68 +1,60 @@
1
- import { jsx as t, jsxs as l } from "react/jsx-runtime";
2
- import { Spinner as u, Alert as b, Card as c, Form as s } from "react-bootstrap";
3
- import { useNotifications as y } from "../../Context/NotificationContext.js";
4
- const N = () => {
5
- const { preferences: n, updatePreferences: i, isLoading: o } = y(), a = (e) => {
6
- n && i({ [e]: !n[e] });
7
- }, d = (e) => {
8
- if (!n) return;
9
- const h = n.mutedEventTypes.includes(e) ? n.mutedEventTypes.filter((p) => p !== e) : [...n.mutedEventTypes, e];
10
- i({ mutedEventTypes: h });
1
+ import { jsx as e, jsxs as o } from "react/jsx-runtime";
2
+ import { Spinner as m, Alert as h, Card as s, Form as a } from "react-bootstrap";
3
+ import { useNotifications as p } from "../../Context/NotificationContext.js";
4
+ import { MUTABLE_EVENT_TYPES as u, isEventMuted as b, toggleMuted as f } from "./mutedEventTypes.js";
5
+ const k = () => {
6
+ const { preferences: t, updatePreferences: i, isLoading: c } = p(), r = (n) => {
7
+ t && i({ [n]: !t[n] });
8
+ }, l = (n) => {
9
+ t && i({
10
+ mutedEventTypes: f(t.mutedEventTypes, n)
11
+ });
11
12
  };
12
- if (o && !n)
13
- return /* @__PURE__ */ t("div", { className: "text-center py-4", children: /* @__PURE__ */ t(u, { animation: "border", size: "sm" }) });
14
- if (!n)
15
- return /* @__PURE__ */ t(b, { variant: "warning", children: "Unable to load notification preferences." });
16
- const m = [
17
- { key: "review_assigned", label: "Review Assignments (Relectures)" },
18
- { key: "project_created", label: "Project Creations" },
19
- { key: "system_alert", label: "System Alerts" }
20
- ];
21
- return /* @__PURE__ */ t(c, { className: "border-0 shadow-sm", children: /* @__PURE__ */ l(c.Body, { className: "p-3", children: [
22
- /* @__PURE__ */ t("h6", { className: "mb-3 text-secondary font-weight-bold", children: "Notification Channels" }),
23
- /* @__PURE__ */ l(s.Group, { className: "mb-3", children: [
24
- /* @__PURE__ */ t(
25
- s.Check,
13
+ return c && !t ? /* @__PURE__ */ e("div", { className: "text-center py-4", children: /* @__PURE__ */ e(m, { animation: "border", size: "sm" }) }) : t ? /* @__PURE__ */ e(s, { className: "border-0 shadow-sm", children: /* @__PURE__ */ o(s.Body, { className: "p-3", children: [
14
+ /* @__PURE__ */ e("h6", { className: "mb-3 text-secondary font-weight-bold", children: "Notification Channels" }),
15
+ /* @__PURE__ */ o(a.Group, { className: "mb-3", children: [
16
+ /* @__PURE__ */ e(
17
+ a.Check,
26
18
  {
27
19
  type: "switch",
28
20
  id: "channel-inapp",
29
21
  label: "In-App Notifications (Real-Time)",
30
- checked: n.inAppEnabled,
31
- onChange: () => a("inAppEnabled"),
22
+ checked: t.inAppEnabled,
23
+ onChange: () => r("inAppEnabled"),
32
24
  className: "mb-2"
33
25
  }
34
26
  ),
35
- /* @__PURE__ */ t(
36
- s.Check,
27
+ /* @__PURE__ */ e(
28
+ a.Check,
37
29
  {
38
30
  type: "switch",
39
31
  id: "channel-email",
40
32
  label: "Email Alerts",
41
- checked: n.emailEnabled,
42
- onChange: () => a("emailEnabled")
33
+ checked: t.emailEnabled,
34
+ onChange: () => r("emailEnabled")
43
35
  }
44
36
  )
45
37
  ] }),
46
- /* @__PURE__ */ t("hr", {}),
47
- /* @__PURE__ */ t("h6", { className: "mb-3 text-secondary font-weight-bold", children: "Mute Event Types" }),
48
- /* @__PURE__ */ t("p", { className: "text-muted small mb-3", children: "Check events you do NOT want to receive notifications for." }),
49
- /* @__PURE__ */ t(s.Group, { children: m.map((e) => {
50
- const r = n.mutedEventTypes.includes(e.key);
51
- return /* @__PURE__ */ t(
52
- s.Check,
38
+ /* @__PURE__ */ e("hr", {}),
39
+ /* @__PURE__ */ e("h6", { className: "mb-3 text-secondary font-weight-bold", children: "Mute Event Types" }),
40
+ /* @__PURE__ */ e("p", { className: "text-muted small mb-3", children: "Check events you do NOT want to receive notifications for." }),
41
+ /* @__PURE__ */ e(a.Group, { children: u.map((n) => {
42
+ const d = b(t.mutedEventTypes, n.key);
43
+ return /* @__PURE__ */ e(
44
+ a.Check,
53
45
  {
54
46
  type: "checkbox",
55
- id: `mute-${e.key}`,
56
- label: e.label,
57
- checked: r,
58
- onChange: () => d(e.key),
47
+ id: `mute-${n.key}`,
48
+ label: n.label,
49
+ checked: d,
50
+ onChange: () => l(n.key),
59
51
  className: "mb-2 text-dark"
60
52
  },
61
- e.key
53
+ n.key
62
54
  );
63
55
  }) })
64
- ] }) });
56
+ ] }) }) : /* @__PURE__ */ e(h, { variant: "warning", children: "Unable to load notification preferences." });
65
57
  };
66
58
  export {
67
- N as NotificationSettings
59
+ k as NotificationSettings
68
60
  };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The event types a user can mute, by the template key the services send.
3
+ * The security alerts of the administration are mandatory: the
4
+ * notification service delivers them whatever is muted here (EV-99).
5
+ */
6
+ export declare const MUTABLE_EVENT_TYPES: {
7
+ key: string;
8
+ label: string;
9
+ }[];
10
+ /** True when the event type is muted, under its key or a former one. */
11
+ export declare function isEventMuted(muted: readonly string[], key: string): boolean;
12
+ /**
13
+ * The muted list once the event type is toggled. Its former keys are
14
+ * dropped either way, so that the list keeps the current key only.
15
+ */
16
+ export declare function toggleMuted(muted: readonly string[], key: string): string[];
@@ -0,0 +1,22 @@
1
+ const c = [
2
+ { key: "review_assigned", label: "Review Assignments (Relectures)" },
3
+ { key: "project_created", label: "Project Creations" },
4
+ {
5
+ key: "admin_alert",
6
+ label: "System Alerts (security alerts are always delivered)"
7
+ }
8
+ ], a = {
9
+ admin_alert: ["system_alert"]
10
+ }, n = (e) => [e, ...a[e] ?? []];
11
+ function i(e, t) {
12
+ return n(t).some((s) => e.includes(s));
13
+ }
14
+ function o(e, t) {
15
+ const s = n(t), r = e.filter((l) => !s.includes(l));
16
+ return i(e, t) ? r : [...r, t];
17
+ }
18
+ export {
19
+ c as MUTABLE_EVENT_TYPES,
20
+ i as isEventMuted,
21
+ o as toggleMuted
22
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@variamosple/variamos-components",
3
3
  "private": false,
4
- "version": "0.2.3",
4
+ "version": "0.2.5",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
7
7
  "types": "dist/main.d.ts",
@@ -38,16 +38,21 @@
38
38
  "eslint": "^9.19.0",
39
39
  "eslint-plugin-react-hooks": "^5.0.0",
40
40
  "eslint-plugin-react-refresh": "^0.4.18",
41
- "glob": "^11.0.1",
41
+ "glob": "^14.0.4",
42
42
  "globals": "^15.14.0",
43
43
  "react": "^19.2.8",
44
44
  "react-dom": "^19.2.8",
45
45
  "typescript": "~5.7.2",
46
46
  "typescript-eslint": "^8.22.0",
47
- "vite": "^6.1.0",
47
+ "vite": "^6.4.5",
48
48
  "vite-plugin-dts": "^5.1.2",
49
49
  "vite-plugin-lib-inject-css": "^2.2.1",
50
- "vitest": "^4.1.11"
50
+ "vitest": "^4.1.12"
51
+ },
52
+ "overrides": {
53
+ "@typescript-eslint/typescript-estree": {
54
+ "minimatch": "^11.0.1"
55
+ }
51
56
  },
52
57
  "allowScripts": {
53
58
  "esbuild@0.25.12": true