@squaredr/fieldcraft-pro 1.1.1 → 1.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/{chunk-RSU3X25P.mjs → chunk-4BX7FCXH.mjs} +416 -21
- package/dist/{chunk-CJBC3KWB.mjs → chunk-GNBXIG63.mjs} +127 -15
- package/dist/form-builder/index.js +125 -13
- package/dist/form-builder/index.mjs +1 -1
- package/dist/index.js +540 -33
- package/dist/index.mjs +3 -3
- package/dist/response-viewer/index.d.mts +10 -0
- package/dist/response-viewer/index.d.ts +10 -0
- package/dist/response-viewer/index.js +414 -19
- package/dist/response-viewer/index.mjs +1 -1
- package/dist/styles.css +1 -1
- package/package.json +7 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from './chunk-QQ4JZGTD.mjs';
|
|
2
2
|
import { requireLicense } from './chunk-VECQKSWS.mjs';
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import { Button, Separator } from '@squaredr/fieldcraft-react';
|
|
3
|
+
import { useState, useEffect } from 'react';
|
|
4
|
+
import { Button, Separator, Input, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@squaredr/fieldcraft-react';
|
|
5
5
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
7
|
// src/response-viewer/clinical-display-data.ts
|
|
@@ -732,16 +732,24 @@ function formatFallbackValue(value) {
|
|
|
732
732
|
}
|
|
733
733
|
|
|
734
734
|
// src/response-viewer/export-utils.ts
|
|
735
|
-
function
|
|
735
|
+
function buildCsvContent(schema, responses, options = {}) {
|
|
736
|
+
const { dateFormat = "locale", columnLabels } = options;
|
|
736
737
|
const questions = getExportableQuestions(schema);
|
|
737
|
-
const headers = [
|
|
738
|
+
const headers = [
|
|
739
|
+
"Submitted",
|
|
740
|
+
...questions.map((q) => columnLabels?.[q.id] ?? q.label),
|
|
741
|
+
"Score"
|
|
742
|
+
];
|
|
738
743
|
const rows = responses.map((r) => {
|
|
739
|
-
const submitted = new Date(r.submittedAt).toLocaleString();
|
|
744
|
+
const submitted = dateFormat === "iso" ? new Date(r.submittedAt).toISOString() : new Date(r.submittedAt).toLocaleString();
|
|
740
745
|
const values = questions.map((q) => formatExportValue(r.values[q.id], q.type));
|
|
741
746
|
const score = r.totalScore != null ? String(r.totalScore) : "";
|
|
742
747
|
return [submitted, ...values, score];
|
|
743
748
|
});
|
|
744
|
-
|
|
749
|
+
return [headers, ...rows].map((row) => row.map(escapeCsvField).join(",")).join("\n");
|
|
750
|
+
}
|
|
751
|
+
function exportToCsv(schema, responses, filename = "responses.csv", options = {}) {
|
|
752
|
+
const csvContent = buildCsvContent(schema, responses, options);
|
|
745
753
|
downloadBlob(csvContent, filename, "text/csv;charset=utf-8;");
|
|
746
754
|
}
|
|
747
755
|
function exportToJson(responses, filename = "responses.json") {
|
|
@@ -836,15 +844,145 @@ function downloadBlob(content, filename, mimeType) {
|
|
|
836
844
|
document.body.removeChild(link);
|
|
837
845
|
URL.revokeObjectURL(url);
|
|
838
846
|
}
|
|
847
|
+
|
|
848
|
+
// src/response-viewer/pagination-utils.ts
|
|
849
|
+
function paginate(items, currentPage, pageSize) {
|
|
850
|
+
const totalItems = items.length;
|
|
851
|
+
const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
|
|
852
|
+
const page = Math.max(1, Math.min(currentPage, totalPages));
|
|
853
|
+
const startIndex = (page - 1) * pageSize;
|
|
854
|
+
const endIndex = Math.min(startIndex + pageSize, totalItems);
|
|
855
|
+
const pageItems = items.slice(startIndex, endIndex);
|
|
856
|
+
return {
|
|
857
|
+
pageItems,
|
|
858
|
+
currentPage: page,
|
|
859
|
+
totalPages,
|
|
860
|
+
startItem: totalItems === 0 ? 0 : startIndex + 1,
|
|
861
|
+
endItem: endIndex,
|
|
862
|
+
totalItems,
|
|
863
|
+
showPagination: totalItems > pageSize
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// src/response-viewer/filter-utils.ts
|
|
868
|
+
function createEmptyFilterState() {
|
|
869
|
+
return { filters: [], dateRange: {} };
|
|
870
|
+
}
|
|
871
|
+
function hasActiveFilters(state) {
|
|
872
|
+
return state.filters.length > 0 || !!state.dateRange.from || !!state.dateRange.to;
|
|
873
|
+
}
|
|
874
|
+
function applyFilters(responses, state) {
|
|
875
|
+
if (!hasActiveFilters(state)) return responses;
|
|
876
|
+
return responses.filter((r) => {
|
|
877
|
+
if (!matchesDateRange(r.submittedAt, state.dateRange)) return false;
|
|
878
|
+
for (const filter of state.filters) {
|
|
879
|
+
if (!matchesFieldFilter(r.values[filter.fieldId], filter)) return false;
|
|
880
|
+
}
|
|
881
|
+
return true;
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
function matchesDateRange(submittedAt, range) {
|
|
885
|
+
if (!range.from && !range.to) return true;
|
|
886
|
+
const submitted = new Date(submittedAt);
|
|
887
|
+
if (range.from) {
|
|
888
|
+
const from = new Date(range.from);
|
|
889
|
+
from.setHours(0, 0, 0, 0);
|
|
890
|
+
if (submitted < from) return false;
|
|
891
|
+
}
|
|
892
|
+
if (range.to) {
|
|
893
|
+
const to = new Date(range.to);
|
|
894
|
+
to.setHours(23, 59, 59, 999);
|
|
895
|
+
if (submitted > to) return false;
|
|
896
|
+
}
|
|
897
|
+
return true;
|
|
898
|
+
}
|
|
899
|
+
function matchesFieldFilter(value, filter) {
|
|
900
|
+
switch (filter.operator) {
|
|
901
|
+
case "equals":
|
|
902
|
+
if (value == null) return false;
|
|
903
|
+
return String(value).toLowerCase() === String(filter.value).toLowerCase();
|
|
904
|
+
case "contains":
|
|
905
|
+
if (value == null) return false;
|
|
906
|
+
return String(value).toLowerCase().includes(String(filter.value).toLowerCase());
|
|
907
|
+
case "gt":
|
|
908
|
+
if (typeof value !== "number") return false;
|
|
909
|
+
return value > Number(filter.value);
|
|
910
|
+
case "lt":
|
|
911
|
+
if (typeof value !== "number") return false;
|
|
912
|
+
return value < Number(filter.value);
|
|
913
|
+
case "is_true":
|
|
914
|
+
return value === true || value === "true" || value === "agreed";
|
|
915
|
+
case "is_false":
|
|
916
|
+
return value === false || value === "false" || value == null;
|
|
917
|
+
default:
|
|
918
|
+
return true;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
var PAGE_SIZE_OPTIONS = [10, 25, 50, 100];
|
|
922
|
+
var FIELD_FILTER_OPERATORS = {
|
|
923
|
+
text: [
|
|
924
|
+
{ label: "equals", value: "equals" },
|
|
925
|
+
{ label: "contains", value: "contains" }
|
|
926
|
+
],
|
|
927
|
+
number: [
|
|
928
|
+
{ label: "equals", value: "equals" },
|
|
929
|
+
{ label: "greater than", value: "gt" },
|
|
930
|
+
{ label: "less than", value: "lt" }
|
|
931
|
+
],
|
|
932
|
+
boolean: [
|
|
933
|
+
{ label: "is true", value: "is_true" },
|
|
934
|
+
{ label: "is false", value: "is_false" }
|
|
935
|
+
],
|
|
936
|
+
select: [
|
|
937
|
+
{ label: "equals", value: "equals" }
|
|
938
|
+
]
|
|
939
|
+
};
|
|
940
|
+
function getOperatorsForType(type) {
|
|
941
|
+
if (type === "number" || type === "slider" || type === "rating" || type === "nps" || type === "opinion_scale" || type === "pain_scale") {
|
|
942
|
+
return FIELD_FILTER_OPERATORS.number;
|
|
943
|
+
}
|
|
944
|
+
if (type === "boolean" || type === "consent") {
|
|
945
|
+
return FIELD_FILTER_OPERATORS.boolean;
|
|
946
|
+
}
|
|
947
|
+
if (type === "single_select" || type === "dropdown" || type === "country_select") {
|
|
948
|
+
return FIELD_FILTER_OPERATORS.select;
|
|
949
|
+
}
|
|
950
|
+
return FIELD_FILTER_OPERATORS.text;
|
|
951
|
+
}
|
|
952
|
+
function operatorNeedsValue(operator) {
|
|
953
|
+
return operator !== "is_true" && operator !== "is_false";
|
|
954
|
+
}
|
|
839
955
|
function ResponseViewerInner({
|
|
840
956
|
schema,
|
|
841
957
|
responses,
|
|
842
958
|
onResponseSelect,
|
|
843
959
|
height = "500px",
|
|
844
|
-
width = "100%"
|
|
960
|
+
width = "100%",
|
|
961
|
+
filename,
|
|
962
|
+
dateFormat,
|
|
963
|
+
columnLabels,
|
|
964
|
+
onExport,
|
|
965
|
+
pageSize = 25
|
|
845
966
|
}) {
|
|
846
967
|
const [viewMode, setViewMode] = useState("table");
|
|
847
968
|
const [selectedResponse, setSelectedResponse] = useState(null);
|
|
969
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
970
|
+
const [effectivePageSize, setEffectivePageSize] = useState(pageSize);
|
|
971
|
+
const [filterState, setFilterState] = useState(createEmptyFilterState);
|
|
972
|
+
const [showFilterPanel, setShowFilterPanel] = useState(false);
|
|
973
|
+
const [draftFieldId, setDraftFieldId] = useState("");
|
|
974
|
+
const [draftOperator, setDraftOperator] = useState("contains");
|
|
975
|
+
const [draftValue, setDraftValue] = useState("");
|
|
976
|
+
useEffect(() => {
|
|
977
|
+
setCurrentPage(1);
|
|
978
|
+
}, [responses]);
|
|
979
|
+
useEffect(() => {
|
|
980
|
+
setEffectivePageSize(pageSize);
|
|
981
|
+
setCurrentPage(1);
|
|
982
|
+
}, [pageSize]);
|
|
983
|
+
const isFiltered = hasActiveFilters(filterState);
|
|
984
|
+
const filteredResponses = applyFilters(responses, filterState);
|
|
985
|
+
const pag = paginate(filteredResponses, currentPage, effectivePageSize);
|
|
848
986
|
function handleSelect(response) {
|
|
849
987
|
setSelectedResponse(response);
|
|
850
988
|
onResponseSelect?.(response);
|
|
@@ -852,6 +990,45 @@ function ResponseViewerInner({
|
|
|
852
990
|
function handleBack() {
|
|
853
991
|
setSelectedResponse(null);
|
|
854
992
|
}
|
|
993
|
+
function handlePageSizeChange(newSize) {
|
|
994
|
+
setEffectivePageSize(Number(newSize));
|
|
995
|
+
setCurrentPage(1);
|
|
996
|
+
}
|
|
997
|
+
function handleAddFilter() {
|
|
998
|
+
if (!draftFieldId) return;
|
|
999
|
+
if (operatorNeedsValue(draftOperator) && !draftValue) return;
|
|
1000
|
+
const newFilter = {
|
|
1001
|
+
fieldId: draftFieldId,
|
|
1002
|
+
operator: draftOperator,
|
|
1003
|
+
value: draftOperator === "gt" || draftOperator === "lt" ? Number(draftValue) : draftValue
|
|
1004
|
+
};
|
|
1005
|
+
setFilterState((prev) => ({
|
|
1006
|
+
...prev,
|
|
1007
|
+
filters: [...prev.filters, newFilter]
|
|
1008
|
+
}));
|
|
1009
|
+
setDraftFieldId("");
|
|
1010
|
+
setDraftOperator("contains");
|
|
1011
|
+
setDraftValue("");
|
|
1012
|
+
setCurrentPage(1);
|
|
1013
|
+
}
|
|
1014
|
+
function handleRemoveFilter(index) {
|
|
1015
|
+
setFilterState((prev) => ({
|
|
1016
|
+
...prev,
|
|
1017
|
+
filters: prev.filters.filter((_, i) => i !== index)
|
|
1018
|
+
}));
|
|
1019
|
+
setCurrentPage(1);
|
|
1020
|
+
}
|
|
1021
|
+
function handleDateRangeChange(field, value) {
|
|
1022
|
+
setFilterState((prev) => ({
|
|
1023
|
+
...prev,
|
|
1024
|
+
dateRange: { ...prev.dateRange, [field]: value || void 0 }
|
|
1025
|
+
}));
|
|
1026
|
+
setCurrentPage(1);
|
|
1027
|
+
}
|
|
1028
|
+
function handleClearAllFilters() {
|
|
1029
|
+
setFilterState(createEmptyFilterState());
|
|
1030
|
+
setCurrentPage(1);
|
|
1031
|
+
}
|
|
855
1032
|
const questions = getAllQuestions2(schema);
|
|
856
1033
|
function getFields(response) {
|
|
857
1034
|
return questions.map((q) => ({
|
|
@@ -869,12 +1046,8 @@ function ResponseViewerInner({
|
|
|
869
1046
|
className: "flex flex-col border border-border rounded-lg overflow-hidden bg-background text-foreground",
|
|
870
1047
|
style: { height: heightValue, width: widthValue },
|
|
871
1048
|
children: [
|
|
872
|
-
!selectedResponse && /* @__PURE__ */ jsxs("div", { className: "flex
|
|
873
|
-
/* @__PURE__ */
|
|
874
|
-
responses.length,
|
|
875
|
-
" response",
|
|
876
|
-
responses.length !== 1 ? "s" : ""
|
|
877
|
-
] }),
|
|
1049
|
+
!selectedResponse && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-2 gap-y-1 px-3 py-2 border-b border-border", children: [
|
|
1050
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground mr-auto", children: isFiltered ? `Showing ${filteredResponses.length} of ${responses.length} responses (filtered)` : `${responses.length} response${responses.length !== 1 ? "s" : ""}` }),
|
|
878
1051
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
879
1052
|
/* @__PURE__ */ jsx(
|
|
880
1053
|
Button,
|
|
@@ -901,15 +1074,50 @@ function ResponseViewerInner({
|
|
|
901
1074
|
onClick: () => setViewMode("card"),
|
|
902
1075
|
children: "Cards"
|
|
903
1076
|
}
|
|
1077
|
+
)
|
|
1078
|
+
] }),
|
|
1079
|
+
/* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5 hidden sm:block" }),
|
|
1080
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1081
|
+
/* @__PURE__ */ jsxs(
|
|
1082
|
+
Button,
|
|
1083
|
+
{
|
|
1084
|
+
variant: showFilterPanel ? "secondary" : "outline",
|
|
1085
|
+
size: "sm",
|
|
1086
|
+
className: cn(
|
|
1087
|
+
"fc-rv-filter__button h-7 text-xs",
|
|
1088
|
+
showFilterPanel && "font-semibold border border-ring"
|
|
1089
|
+
),
|
|
1090
|
+
onClick: () => setShowFilterPanel((v) => !v),
|
|
1091
|
+
children: [
|
|
1092
|
+
"Filter",
|
|
1093
|
+
isFiltered ? ` (${filterState.filters.length + (filterState.dateRange.from || filterState.dateRange.to ? 1 : 0)})` : ""
|
|
1094
|
+
]
|
|
1095
|
+
}
|
|
904
1096
|
),
|
|
905
|
-
/* @__PURE__ */ jsx(
|
|
1097
|
+
isFiltered && /* @__PURE__ */ jsx(
|
|
1098
|
+
Button,
|
|
1099
|
+
{
|
|
1100
|
+
variant: "ghost",
|
|
1101
|
+
size: "sm",
|
|
1102
|
+
className: "h-7 text-xs text-destructive",
|
|
1103
|
+
onClick: handleClearAllFilters,
|
|
1104
|
+
children: "Clear all"
|
|
1105
|
+
}
|
|
1106
|
+
)
|
|
1107
|
+
] }),
|
|
1108
|
+
/* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "h-5 hidden sm:block" }),
|
|
1109
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
906
1110
|
/* @__PURE__ */ jsx(
|
|
907
1111
|
Button,
|
|
908
1112
|
{
|
|
909
1113
|
variant: "outline",
|
|
910
1114
|
size: "sm",
|
|
911
1115
|
className: "h-7 text-xs",
|
|
912
|
-
onClick: () =>
|
|
1116
|
+
onClick: () => {
|
|
1117
|
+
const csvFilename = filename ? filename.replace(/\.\w+$/, ".csv") : "responses.csv";
|
|
1118
|
+
exportToCsv(schema, responses, csvFilename, { dateFormat, columnLabels });
|
|
1119
|
+
onExport?.("csv", responses.length);
|
|
1120
|
+
},
|
|
913
1121
|
disabled: responses.length === 0,
|
|
914
1122
|
children: "Export CSV"
|
|
915
1123
|
}
|
|
@@ -920,14 +1128,146 @@ function ResponseViewerInner({
|
|
|
920
1128
|
variant: "outline",
|
|
921
1129
|
size: "sm",
|
|
922
1130
|
className: "h-7 text-xs",
|
|
923
|
-
onClick: () =>
|
|
1131
|
+
onClick: () => {
|
|
1132
|
+
const jsonFilename = filename ? filename.replace(/\.\w+$/, ".json") : "responses.json";
|
|
1133
|
+
exportToJson(responses, jsonFilename);
|
|
1134
|
+
onExport?.("json", responses.length);
|
|
1135
|
+
},
|
|
924
1136
|
disabled: responses.length === 0,
|
|
925
1137
|
children: "Export JSON"
|
|
926
1138
|
}
|
|
927
1139
|
)
|
|
928
1140
|
] })
|
|
929
1141
|
] }),
|
|
930
|
-
/* @__PURE__ */
|
|
1142
|
+
!selectedResponse && showFilterPanel && /* @__PURE__ */ jsxs("div", { className: "fc-rv-filter__panel px-3 py-2 border-b border-border bg-muted/50", children: [
|
|
1143
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 mb-2", children: [
|
|
1144
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground w-16 shrink-0", children: "Date:" }),
|
|
1145
|
+
/* @__PURE__ */ jsx(
|
|
1146
|
+
Input,
|
|
1147
|
+
{
|
|
1148
|
+
type: "date",
|
|
1149
|
+
className: "h-7 text-xs w-36",
|
|
1150
|
+
value: filterState.dateRange.from ?? "",
|
|
1151
|
+
onChange: (e) => handleDateRangeChange("from", e.target.value)
|
|
1152
|
+
}
|
|
1153
|
+
),
|
|
1154
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "to" }),
|
|
1155
|
+
/* @__PURE__ */ jsx(
|
|
1156
|
+
Input,
|
|
1157
|
+
{
|
|
1158
|
+
type: "date",
|
|
1159
|
+
className: "h-7 text-xs w-36",
|
|
1160
|
+
value: filterState.dateRange.to ?? "",
|
|
1161
|
+
onChange: (e) => handleDateRangeChange("to", e.target.value)
|
|
1162
|
+
}
|
|
1163
|
+
)
|
|
1164
|
+
] }),
|
|
1165
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1166
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground w-16 shrink-0", children: "Field:" }),
|
|
1167
|
+
/* @__PURE__ */ jsxs(
|
|
1168
|
+
Select,
|
|
1169
|
+
{
|
|
1170
|
+
value: draftFieldId || void 0,
|
|
1171
|
+
onValueChange: (val) => {
|
|
1172
|
+
setDraftFieldId(val);
|
|
1173
|
+
const q = questions.find((q2) => q2.id === val);
|
|
1174
|
+
if (q) {
|
|
1175
|
+
const ops = getOperatorsForType(q.type);
|
|
1176
|
+
setDraftOperator(ops[0].value);
|
|
1177
|
+
}
|
|
1178
|
+
setDraftValue("");
|
|
1179
|
+
},
|
|
1180
|
+
children: [
|
|
1181
|
+
/* @__PURE__ */ jsx(SelectTrigger, { size: "sm", className: "h-7 text-xs w-40", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select field" }) }),
|
|
1182
|
+
/* @__PURE__ */ jsx(SelectContent, { children: questions.map((q) => /* @__PURE__ */ jsx(SelectItem, { value: q.id, children: q.label }, q.id)) })
|
|
1183
|
+
]
|
|
1184
|
+
}
|
|
1185
|
+
),
|
|
1186
|
+
draftFieldId && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1187
|
+
/* @__PURE__ */ jsxs(
|
|
1188
|
+
Select,
|
|
1189
|
+
{
|
|
1190
|
+
value: draftOperator,
|
|
1191
|
+
onValueChange: (val) => setDraftOperator(val),
|
|
1192
|
+
children: [
|
|
1193
|
+
/* @__PURE__ */ jsx(SelectTrigger, { size: "sm", className: "h-7 text-xs w-32", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1194
|
+
/* @__PURE__ */ jsx(SelectContent, { children: getOperatorsForType(
|
|
1195
|
+
questions.find((q) => q.id === draftFieldId)?.type ?? "short_text"
|
|
1196
|
+
).map((op) => /* @__PURE__ */ jsx(SelectItem, { value: op.value, children: op.label }, op.value)) })
|
|
1197
|
+
]
|
|
1198
|
+
}
|
|
1199
|
+
),
|
|
1200
|
+
operatorNeedsValue(draftOperator) && /* @__PURE__ */ jsx(
|
|
1201
|
+
Input,
|
|
1202
|
+
{
|
|
1203
|
+
className: "h-7 text-xs w-32",
|
|
1204
|
+
placeholder: "Value",
|
|
1205
|
+
value: draftValue,
|
|
1206
|
+
onChange: (e) => setDraftValue(e.target.value),
|
|
1207
|
+
onKeyDown: (e) => e.key === "Enter" && handleAddFilter()
|
|
1208
|
+
}
|
|
1209
|
+
),
|
|
1210
|
+
/* @__PURE__ */ jsx(
|
|
1211
|
+
Button,
|
|
1212
|
+
{
|
|
1213
|
+
variant: "outline",
|
|
1214
|
+
size: "sm",
|
|
1215
|
+
className: "h-7 text-xs",
|
|
1216
|
+
onClick: handleAddFilter,
|
|
1217
|
+
children: "Add"
|
|
1218
|
+
}
|
|
1219
|
+
)
|
|
1220
|
+
] })
|
|
1221
|
+
] })
|
|
1222
|
+
] }),
|
|
1223
|
+
!selectedResponse && isFiltered && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 px-3 py-1.5 border-b border-border flex-wrap", children: [
|
|
1224
|
+
filterState.dateRange.from && /* @__PURE__ */ jsxs(
|
|
1225
|
+
"span",
|
|
1226
|
+
{
|
|
1227
|
+
className: "fc-rv-filter__chip inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-accent text-xs text-foreground cursor-pointer hover:bg-destructive/20",
|
|
1228
|
+
onClick: () => handleDateRangeChange("from", ""),
|
|
1229
|
+
children: [
|
|
1230
|
+
"From: ",
|
|
1231
|
+
filterState.dateRange.from,
|
|
1232
|
+
" \xD7"
|
|
1233
|
+
]
|
|
1234
|
+
}
|
|
1235
|
+
),
|
|
1236
|
+
filterState.dateRange.to && /* @__PURE__ */ jsxs(
|
|
1237
|
+
"span",
|
|
1238
|
+
{
|
|
1239
|
+
className: "fc-rv-filter__chip inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-accent text-xs text-foreground cursor-pointer hover:bg-destructive/20",
|
|
1240
|
+
onClick: () => handleDateRangeChange("to", ""),
|
|
1241
|
+
children: [
|
|
1242
|
+
"To: ",
|
|
1243
|
+
filterState.dateRange.to,
|
|
1244
|
+
" \xD7"
|
|
1245
|
+
]
|
|
1246
|
+
}
|
|
1247
|
+
),
|
|
1248
|
+
filterState.filters.map((f, i) => {
|
|
1249
|
+
const q = questions.find((q2) => q2.id === f.fieldId);
|
|
1250
|
+
const label = q?.label ?? f.fieldId;
|
|
1251
|
+
const opLabel = f.operator.replace("_", " ");
|
|
1252
|
+
const valueStr = operatorNeedsValue(f.operator) ? ` "${f.value}"` : "";
|
|
1253
|
+
return /* @__PURE__ */ jsxs(
|
|
1254
|
+
"span",
|
|
1255
|
+
{
|
|
1256
|
+
className: "fc-rv-filter__chip inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-accent text-xs text-foreground cursor-pointer hover:bg-destructive/20",
|
|
1257
|
+
onClick: () => handleRemoveFilter(i),
|
|
1258
|
+
children: [
|
|
1259
|
+
label,
|
|
1260
|
+
" ",
|
|
1261
|
+
opLabel,
|
|
1262
|
+
valueStr,
|
|
1263
|
+
" \xD7"
|
|
1264
|
+
]
|
|
1265
|
+
},
|
|
1266
|
+
i
|
|
1267
|
+
);
|
|
1268
|
+
})
|
|
1269
|
+
] }),
|
|
1270
|
+
/* @__PURE__ */ jsx("div", { className: cn("flex-1 min-w-0 overflow-auto", selectedResponse && "p-4"), children: selectedResponse ? /* @__PURE__ */ jsx(
|
|
931
1271
|
ResponseDetail,
|
|
932
1272
|
{
|
|
933
1273
|
response: selectedResponse,
|
|
@@ -938,11 +1278,11 @@ function ResponseViewerInner({
|
|
|
938
1278
|
ResponseTable,
|
|
939
1279
|
{
|
|
940
1280
|
schema,
|
|
941
|
-
responses,
|
|
1281
|
+
responses: pag.pageItems,
|
|
942
1282
|
onRowClick: handleSelect
|
|
943
1283
|
}
|
|
944
1284
|
) : /* @__PURE__ */ jsxs("div", { className: "grid gap-3 p-3 grid-cols-[repeat(auto-fill,minmax(280px,1fr))]", children: [
|
|
945
|
-
|
|
1285
|
+
pag.pageItems.map((response, idx) => /* @__PURE__ */ jsx(
|
|
946
1286
|
ResponseCard,
|
|
947
1287
|
{
|
|
948
1288
|
response,
|
|
@@ -951,8 +1291,63 @@ function ResponseViewerInner({
|
|
|
951
1291
|
},
|
|
952
1292
|
response.sessionToken || idx
|
|
953
1293
|
)),
|
|
954
|
-
|
|
955
|
-
] }) })
|
|
1294
|
+
filteredResponses.length === 0 && /* @__PURE__ */ jsx("div", { className: "p-8 text-center text-muted-foreground", children: isFiltered ? "No matching responses" : "No responses yet" })
|
|
1295
|
+
] }) }),
|
|
1296
|
+
!selectedResponse && pag.showPagination && /* @__PURE__ */ jsxs("div", { className: "fc-rv-pagination flex flex-wrap items-center gap-x-2 gap-y-1 px-3 py-2 border-t border-border", children: [
|
|
1297
|
+
/* @__PURE__ */ jsxs("div", { className: "fc-rv-pagination__info text-xs text-muted-foreground mr-auto", children: [
|
|
1298
|
+
"Showing ",
|
|
1299
|
+
pag.startItem,
|
|
1300
|
+
"\u2013",
|
|
1301
|
+
pag.endItem,
|
|
1302
|
+
" of ",
|
|
1303
|
+
pag.totalItems,
|
|
1304
|
+
" responses"
|
|
1305
|
+
] }),
|
|
1306
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1307
|
+
/* @__PURE__ */ jsxs("div", { className: "fc-rv-pagination__size-select flex items-center gap-1", children: [
|
|
1308
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Per page:" }),
|
|
1309
|
+
/* @__PURE__ */ jsxs(
|
|
1310
|
+
Select,
|
|
1311
|
+
{
|
|
1312
|
+
value: String(effectivePageSize),
|
|
1313
|
+
onValueChange: handlePageSizeChange,
|
|
1314
|
+
children: [
|
|
1315
|
+
/* @__PURE__ */ jsx(SelectTrigger, { size: "sm", className: "h-7 w-16 text-xs", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1316
|
+
/* @__PURE__ */ jsx(SelectContent, { children: PAGE_SIZE_OPTIONS.map((size) => /* @__PURE__ */ jsx(SelectItem, { value: String(size), children: size }, size)) })
|
|
1317
|
+
]
|
|
1318
|
+
}
|
|
1319
|
+
)
|
|
1320
|
+
] }),
|
|
1321
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
1322
|
+
"Page ",
|
|
1323
|
+
pag.currentPage,
|
|
1324
|
+
" of ",
|
|
1325
|
+
pag.totalPages
|
|
1326
|
+
] }),
|
|
1327
|
+
/* @__PURE__ */ jsx(
|
|
1328
|
+
Button,
|
|
1329
|
+
{
|
|
1330
|
+
variant: "outline",
|
|
1331
|
+
size: "sm",
|
|
1332
|
+
className: "fc-rv-pagination__button h-7 text-xs",
|
|
1333
|
+
onClick: () => setCurrentPage((p) => p - 1),
|
|
1334
|
+
disabled: pag.currentPage <= 1,
|
|
1335
|
+
children: "Previous"
|
|
1336
|
+
}
|
|
1337
|
+
),
|
|
1338
|
+
/* @__PURE__ */ jsx(
|
|
1339
|
+
Button,
|
|
1340
|
+
{
|
|
1341
|
+
variant: "outline",
|
|
1342
|
+
size: "sm",
|
|
1343
|
+
className: "fc-rv-pagination__button h-7 text-xs",
|
|
1344
|
+
onClick: () => setCurrentPage((p) => p + 1),
|
|
1345
|
+
disabled: pag.currentPage >= pag.totalPages,
|
|
1346
|
+
children: "Next"
|
|
1347
|
+
}
|
|
1348
|
+
)
|
|
1349
|
+
] })
|
|
1350
|
+
] })
|
|
956
1351
|
]
|
|
957
1352
|
}
|
|
958
1353
|
);
|