@marktiderman/genesis-forms-studio 0.1.1 → 0.3.0

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.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { SAVE_STATE_LABEL, readinessChip, prefilledUrl, embedSnippet, lowThisWeek, NO_FILTERS, lowScaleAtMost, dayLabel } from './chunk-ZHH2UHOS.js';
3
- export { NO_FILTERS, SAVE_STATE_LABEL, answerCell, answerColumns, answersByKey, carryAnswers, comparisonFilter, dayLabel, embedSnippet, filterResponses, followUpOf, formAvailability, formCounts, formNavItems, formStatusChip, hasLowRating, hasLowRatingExcept, headlineCards, headlineNumbers, highestRating, isFiltered, isFull, lowRatingAtMost, lowRatingLabel, lowScaleAtMost, lowThisWeek, lowestRating, needsFollowUp, parseInstant, plainLabel, prefilledUrl, presetRange, readinessChip, readinessOf, summarizeResponses, takesResponses, toCsv, toResponseFilter, valuesByKey } from './chunk-ZHH2UHOS.js';
2
+ import { SAVE_STATE_LABEL, readinessChip, prefilledUrl, embedSnippet, STATUS_FILTER_LABEL, lowThisWeek, NO_FILTERS, lowScaleAtMost, dayLabel } from './chunk-HP77TYTT.js';
3
+ export { NO_FILTERS, RESPONSE_STATUS_LABEL, SAVE_STATE_LABEL, SLUG_PATTERN, STATUS_FILTER_LABEL, answerCell, answerColumns, answersByKey, carryAnswers, comparisonFilter, copyTitle, dayLabel, dropOffByQuestion, embedSnippet, filterResponses, followUpOf, formAvailability, formCounts, formNavItems, formStatusChip, freeSlug, hasLowRating, hasLowRatingExcept, headlineCards, headlineNumbers, highestRating, isFiltered, isFull, lowRatingAtMost, lowRatingLabel, lowScaleAtMost, lowThisWeek, lowestRating, matchesStatus, needsFollowUp, offersSubmitAnother, parseInstant, plainLabel, prefilledUrl, presetRange, readinessChip, readinessOf, slugFromTitle, summarizeResponses, takesResponses, toCsv, toResponseFilter, validateSlug, valuesByKey } from './chunk-HP77TYTT.js';
4
4
  import { StatusBadge, cn, Badge, Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbSeparator, BreadcrumbPage, Button, Separator, ToggleGroup, ToggleGroupItem, Skeleton, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Tabs, TabsList, TabsTrigger, TabsContent, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Label, Textarea, Input, EmptyState, toast } from '@marktiderman/genesis-ui';
5
- import { Settings, Inbox, GitBranch, ListChecks, Check, Loader2, TriangleAlert, Undo2, Redo2, Eye, Share2, Rocket, CircleAlert, CircleCheck, Star, ExternalLink, Printer, Copy, CalendarDays, X, CircleDot, SquareCheck, Gauge, Type } from 'lucide-react';
5
+ import { Settings, Inbox, GitBranch, ListChecks, Check, Loader2, TriangleAlert, Undo2, Redo2, Eye, Share2, Rocket, CircleAlert, CircleCheck, Star, ExternalLink, Printer, Copy, CalendarDays, CircleDashed, X, CircleDot, SquareCheck, Gauge, Type, ChartNoAxesColumnDecreasing, ImageOff, Image } from 'lucide-react';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
  import { useId, useState, useEffect } from 'react';
8
8
 
@@ -604,6 +604,7 @@ function ResponsesFilters({
604
604
  choices = [],
605
605
  ratingMax = null,
606
606
  lowLabel = null,
607
+ statuses = false,
607
608
  filtered
608
609
  }) {
609
610
  const ratings = ratingMax ? Array.from({ length: Math.min(ratingMax, 10) - 1 }, (_, i) => i + 1) : [];
@@ -640,6 +641,20 @@ function ResponsesFilters({
640
641
  }
641
642
  )
642
643
  ] }) : null,
644
+ statuses ? /* @__PURE__ */ jsxs(
645
+ Select,
646
+ {
647
+ value: value.status ?? "counted",
648
+ onValueChange: (status) => onChange({ ...value, status }),
649
+ children: [
650
+ /* @__PURE__ */ jsxs(SelectTrigger, { className: TRIGGER, "aria-label": "Status", "data-testid": "responses-status", children: [
651
+ /* @__PURE__ */ jsx(CircleDashed, { className: "size-4 text-muted-foreground", "aria-hidden": true }),
652
+ /* @__PURE__ */ jsx(SelectValue, {})
653
+ ] }),
654
+ /* @__PURE__ */ jsx(SelectContent, { children: Object.keys(STATUS_FILTER_LABEL).map((s) => /* @__PURE__ */ jsx(SelectItem, { value: s, children: STATUS_FILTER_LABEL[s] }, s)) })
655
+ ]
656
+ }
657
+ ) : null,
643
658
  choices.filter((c) => c.options.length > 0).map((c) => /* @__PURE__ */ jsxs(
644
659
  Select,
645
660
  {
@@ -879,7 +894,118 @@ function QuestionSummaryCards({
879
894
  }
880
895
  return /* @__PURE__ */ jsx("div", { className: cn("flex min-w-0 flex-col gap-3", className), children: summaries.map((s) => /* @__PURE__ */ jsx(QuestionSummaryCard, { summary: s, today, answerMeta }, s.key)) });
881
896
  }
897
+ var plural2 = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
898
+ function DropOffByQuestion({
899
+ dropOffs,
900
+ isLoading = false,
901
+ title = "Drop-off by question",
902
+ className
903
+ }) {
904
+ if (isLoading) return /* @__PURE__ */ jsx(Skeleton, { className: cn("h-40 w-full rounded-lg", className) });
905
+ const started = dropOffs[0]?.views ?? 0;
906
+ const worst = Math.max(0, ...dropOffs.map((d) => d.stopped));
907
+ return /* @__PURE__ */ jsx(
908
+ SummaryCard,
909
+ {
910
+ icon: /* @__PURE__ */ jsx(ChartNoAxesColumnDecreasing, {}),
911
+ title,
912
+ meta: started ? `${plural2(started, "person", "people")} started` : "Nobody has started yet",
913
+ className,
914
+ testID: "drop-off",
915
+ children: started ? /* @__PURE__ */ jsx("ol", { className: "space-y-3", children: dropOffs.map((d, i) => /* @__PURE__ */ jsxs("li", { className: "space-y-1.5 text-sm", "data-testid": `drop-off-${d.key}`, children: [
916
+ /* @__PURE__ */ jsxs("p", { className: "flex items-baseline justify-between gap-3", children: [
917
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 break-words", children: [
918
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground tabular-nums", children: [
919
+ i + 1,
920
+ ". "
921
+ ] }),
922
+ d.label || "Untitled question"
923
+ ] }),
924
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 whitespace-nowrap tabular-nums text-muted-foreground", children: d.dropOff === null ? "\u2013" : `${d.dropOff}% drop-off` })
925
+ ] }),
926
+ /* @__PURE__ */ jsx("span", { className: "block h-2 overflow-hidden rounded-full bg-muted", "aria-hidden": true, children: /* @__PURE__ */ jsx(
927
+ "span",
928
+ {
929
+ className: cn(
930
+ "block h-full rounded-full",
931
+ d.stopped && d.stopped === worst ? "bg-warning" : "bg-primary"
932
+ ),
933
+ style: { width: `${d.dropOff ?? 0}%` }
934
+ }
935
+ ) }),
936
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
937
+ plural2(d.views, "view"),
938
+ " \xB7 ",
939
+ d.stopped,
940
+ " stopped here"
941
+ ] })
942
+ ] }, d.key)) }) : null
943
+ }
944
+ );
945
+ }
946
+ var UNDRAWABLE = /* @__PURE__ */ new Set(["image/heic", "image/heif"]);
947
+ function Photo({ photo, resolveUrl }) {
948
+ const [resolved, setResolved] = useState({ state: "loading" });
949
+ const [broken, setBroken] = useState(false);
950
+ useEffect(() => {
951
+ let live = true;
952
+ setResolved({ state: "loading" });
953
+ setBroken(false);
954
+ resolveUrl(photo).then((url) => live && setResolved(url ? { state: "ready", url } : { state: "missing" })).catch(() => live && setResolved({ state: "missing" }));
955
+ return () => {
956
+ live = false;
957
+ };
958
+ }, [photo.id]);
959
+ const tile = "relative size-28 overflow-hidden rounded-md border border-border bg-muted";
960
+ if (resolved.state === "loading") return /* @__PURE__ */ jsx(Skeleton, { className: "size-28 rounded-md", "aria-label": `Loading ${photo.name}` });
961
+ if (resolved.state === "missing") {
962
+ return /* @__PURE__ */ jsxs("div", { className: cn(tile, "flex flex-col items-center justify-center gap-1 p-2 text-center text-xs text-muted-foreground"), children: [
963
+ /* @__PURE__ */ jsx(ImageOff, { className: "size-5", "aria-hidden": "true" }),
964
+ /* @__PURE__ */ jsx("span", { children: "Photo unavailable" })
965
+ ] });
966
+ }
967
+ if (broken || UNDRAWABLE.has(photo.type.toLowerCase())) {
968
+ return /* @__PURE__ */ jsxs("div", { className: cn(tile, "flex flex-col items-center justify-center gap-1 p-2 text-center text-xs"), children: [
969
+ /* @__PURE__ */ jsx(Image, { className: "size-5 text-muted-foreground", "aria-hidden": "true" }),
970
+ /* @__PURE__ */ jsx("span", { className: "line-clamp-2 break-all text-muted-foreground", children: photo.name }),
971
+ /* @__PURE__ */ jsxs(
972
+ "a",
973
+ {
974
+ href: resolved.url,
975
+ target: "_blank",
976
+ rel: "noreferrer",
977
+ className: "inline-flex items-center gap-1 font-medium text-primary underline-offset-4 hover:underline",
978
+ children: [
979
+ "Open ",
980
+ /* @__PURE__ */ jsx(ExternalLink, { className: "size-3", "aria-hidden": "true" }),
981
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: photo.name })
982
+ ]
983
+ }
984
+ )
985
+ ] });
986
+ }
987
+ return /* @__PURE__ */ jsx(
988
+ "a",
989
+ {
990
+ href: resolved.url,
991
+ target: "_blank",
992
+ rel: "noreferrer",
993
+ className: cn(tile, "block outline-none ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"),
994
+ title: photo.name,
995
+ children: /* @__PURE__ */ jsx("img", { src: resolved.url, alt: photo.name, loading: "lazy", className: "size-full object-cover", onError: () => setBroken(true) })
996
+ }
997
+ );
998
+ }
999
+ function PhotoAnswer({
1000
+ photos,
1001
+ resolveUrl,
1002
+ className,
1003
+ testID
1004
+ }) {
1005
+ if (!photos.length) return null;
1006
+ return /* @__PURE__ */ jsx("ul", { className: cn("flex flex-wrap gap-3", className), "aria-label": "Photos", "data-testid": testID, children: photos.map((photo) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Photo, { photo, resolveUrl }) }, photo.id)) });
1007
+ }
882
1008
 
883
- export { Chip, FORM_NAV_ICONS, FollowUpList, HeadlineNumbers, QuestionSummaryCard, QuestionSummaryCards, ReadinessChip, ResourceHeader, ResourceSettingsPage, ResponsesFilters, ResponsesToolbar, SettingsSection, ShareDialog, SummaryCard };
1009
+ export { Chip, DropOffByQuestion, FORM_NAV_ICONS, FollowUpList, HeadlineNumbers, PhotoAnswer, QuestionSummaryCard, QuestionSummaryCards, ReadinessChip, ResourceHeader, ResourceSettingsPage, ResponsesFilters, ResponsesToolbar, SettingsSection, ShareDialog, SummaryCard };
884
1010
  //# sourceMappingURL=index.js.map
885
1011
  //# sourceMappingURL=index.js.map