@stoker-platform/web-app 0.5.51 → 0.5.53

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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @stoker-platform/web-app
2
2
 
3
+ ## 0.5.53
4
+
5
+ ### Patch Changes
6
+
7
+ - feat: add custom list actions
8
+ - @stoker-platform/node-client@0.5.35
9
+ - @stoker-platform/utils@0.5.29
10
+ - @stoker-platform/web-client@0.5.31
11
+
12
+ ## 0.5.52
13
+
14
+ ### Patch Changes
15
+
16
+ - feat: add new lg image size
17
+ - @stoker-platform/node-client@0.5.34
18
+ - @stoker-platform/utils@0.5.28
19
+ - @stoker-platform/web-client@0.5.30
20
+
3
21
  ## 0.5.51
4
22
 
5
23
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stoker-platform/web-app",
3
- "version": "0.5.51",
3
+ "version": "0.5.53",
4
4
  "type": "module",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "scripts": {
@@ -51,9 +51,9 @@
51
51
  "@radix-ui/react-tooltip": "^1.2.8",
52
52
  "@react-google-maps/api": "^2.20.8",
53
53
  "@sentry/react": "^10.43.0",
54
- "@stoker-platform/node-client": "0.5.33",
55
- "@stoker-platform/utils": "0.5.27",
56
- "@stoker-platform/web-client": "0.5.29",
54
+ "@stoker-platform/node-client": "0.5.35",
55
+ "@stoker-platform/utils": "0.5.29",
56
+ "@stoker-platform/web-client": "0.5.31",
57
57
  "@tanstack/react-table": "^8.21.3",
58
58
  "@types/react": "18.3.13",
59
59
  "@types/react-dom": "18.3.1",
@@ -4,6 +4,7 @@ import {
4
4
  CardsConfig,
5
5
  CollectionField,
6
6
  CollectionSchema,
7
+ CustomListAction,
7
8
  Filter,
8
9
  FormList,
9
10
  ImagesConfig,
@@ -206,6 +207,7 @@ function Collection({
206
207
  const [isOfflineDisabled, setIsOfflineDisabled] = useState<boolean | undefined>(undefined)
207
208
  const [restrictExport, setRestrictExport] = useState<StokerRole[] | undefined>(undefined)
208
209
  const [disableCreate, setDisableCreate] = useState<boolean>(false)
210
+ const [customListActions, setCustomListActions] = useState<CustomListAction[] | undefined>(undefined)
209
211
 
210
212
  const [table, setTable] = useState<Table<StokerRecord> | undefined>(undefined)
211
213
  const [listConfig, setListConfig] = useState<ListConfig | undefined>(undefined)
@@ -840,6 +842,9 @@ function Collection({
840
842
  )
841
843
  setDisableCreate(!!disableCreate)
842
844
  const filters = (await getCachedConfigValue(customization, [...collectionAdminPath, "filters"])) || []
845
+ const customListActions =
846
+ (await getCachedConfigValue(customization, [...collectionAdminPath, "customListActions"])) || []
847
+ setCustomListActions(customListActions)
843
848
 
844
849
  const statusField = await getCachedConfigValue(customization, [...collectionAdminPath, "statusField"])
845
850
  setStatusField(statusField)
@@ -1883,26 +1888,84 @@ function Collection({
1883
1888
  )}
1884
1889
  </ToggleGroup>
1885
1890
  )}
1886
- {!formList &&
1887
- tab === "list" &&
1888
- (!restrictExport || restrictExport.includes(permissions.Role)) && (
1889
- <>
1890
- <Button
1891
- type="button"
1892
- size="sm"
1893
- variant="outline"
1894
- disabled={
1895
- !list.default?.length ||
1896
- isRouteLoading.has(location.pathname)
1897
- }
1898
- className="hidden sm:flex h-7 gap-1"
1899
- onClick={handleExport}
1900
- >
1901
- <File className="h-3.5 w-3.5" />
1902
- <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
1903
- Export
1904
- </span>
1905
- </Button>
1891
+ {!formList && tab === "list" && (
1892
+ <>
1893
+ {customListActions &&
1894
+ customListActions.some(
1895
+ (action) => !action.condition || action.condition(),
1896
+ ) ? (
1897
+ <DropdownMenu>
1898
+ <DropdownMenuTrigger>
1899
+ <Button
1900
+ type="button"
1901
+ size="sm"
1902
+ variant="outline"
1903
+ className="hidden sm:flex h-7 gap-1"
1904
+ >
1905
+ <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
1906
+ Actions
1907
+ </span>
1908
+ <ChevronsUpDown className="h-3.5 w-3.5" />
1909
+ </Button>
1910
+ </DropdownMenuTrigger>
1911
+ <DropdownMenuContent>
1912
+ {(!restrictExport ||
1913
+ restrictExport.includes(permissions.Role)) && (
1914
+ <DropdownMenuItem
1915
+ key="export"
1916
+ onClick={handleExport}
1917
+ disabled={
1918
+ !list.default?.length ||
1919
+ isRouteLoading.has(location.pathname)
1920
+ }
1921
+ >
1922
+ <File className="h-3.5 w-3.5 shrink-0 mr-1" />
1923
+ <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
1924
+ Export
1925
+ </span>
1926
+ </DropdownMenuItem>
1927
+ )}
1928
+ {customListActions
1929
+ .filter(
1930
+ (action) => !action.condition || action.condition(),
1931
+ )
1932
+ .map((action) => (
1933
+ <DropdownMenuItem
1934
+ key={action.title}
1935
+ onClick={action.action}
1936
+ >
1937
+ {action.icon &&
1938
+ createElement(action.icon, {
1939
+ className: "h-3.5 w-3.5 shrink-0 mr-1",
1940
+ })}
1941
+ <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
1942
+ {action.title}
1943
+ </span>
1944
+ </DropdownMenuItem>
1945
+ ))}
1946
+ </DropdownMenuContent>
1947
+ </DropdownMenu>
1948
+ ) : (
1949
+ (!restrictExport || restrictExport.includes(permissions.Role)) && (
1950
+ <Button
1951
+ type="button"
1952
+ size="sm"
1953
+ variant="outline"
1954
+ disabled={
1955
+ !list.default?.length ||
1956
+ isRouteLoading.has(location.pathname)
1957
+ }
1958
+ className="hidden sm:flex h-7 gap-1"
1959
+ onClick={handleExport}
1960
+ >
1961
+ <File className="h-3.5 w-3.5" />
1962
+ <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
1963
+ Export
1964
+ </span>
1965
+ </Button>
1966
+ )
1967
+ )}
1968
+ {(!restrictExport || restrictExport.includes(permissions.Role)) && (
1906
1969
  <CSVLink
1907
1970
  ref={csvLinkRef}
1908
1971
  className="hidden"
@@ -1911,8 +1974,9 @@ function Collection({
1911
1974
  filename={`${collectionTitle}.csv`}
1912
1975
  target="_blank"
1913
1976
  />
1914
- </>
1915
- )}
1977
+ )}
1978
+ </>
1979
+ )}
1916
1980
  {(tab === "cards" || tab === "images") && (
1917
1981
  <DropdownMenu>
1918
1982
  <DropdownMenuTrigger asChild>
package/src/Images.tsx CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  onStokerPermissionsChange,
18
18
  subscribeOne,
19
19
  } from "@stoker-platform/web-client"
20
- import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
20
+ import { createElement, memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
21
21
  import { Card, CardContent, CardHeader, CardTitle } from "./components/ui/card"
22
22
  import { useGoToRecord } from "./utils/goToRecord"
23
23
  import { LoadingSpinner } from "./components/ui/loading-spinner"
@@ -228,30 +228,57 @@ const Row = ({ index, style, data }: RowProps) => {
228
228
  </button>
229
229
  </CardHeader>
230
230
  <CardContent className="pb-3 md:pb-4">
231
- <div className={cn("grid", "gap-4", size)}>
232
- {isAssigning && assignable && (assignable.isAvailable(record) || checked) && (
233
- <div>
234
- <div className="flex items-center justify-center space-x-3 min-h-8">
235
- <Switch
236
- id={`${record.id}-assigned`}
237
- className="data-[state=checked]:bg-blue-500"
238
- checked={checked}
239
- disabled={checkedDisabled}
240
- onCheckedChange={(checked) => handleCheckedChange(checked, record)}
241
- />
242
- {imagesConfig.size !== "sm" && (
243
- <Label htmlFor={`${record.id}-assigned`}>Assigned</Label>
244
- )}
245
- </div>
231
+ {imagesConfig.customComponent &&
232
+ (!imagesConfig.customComponent.condition ||
233
+ imagesConfig.customComponent.condition(
234
+ relationCollection,
235
+ relationParent,
236
+ isAssigning,
237
+ )) && (
238
+ <div
239
+ className={cn(
240
+ `h-[${imagesConfig.customComponent.height}px]`,
241
+ "overflow-hidden",
242
+ )}
243
+ >
244
+ {createElement(imagesConfig.customComponent.component, {
245
+ record,
246
+ parentRecord: relationParent,
247
+ collection,
248
+ parentCollection: relationCollection,
249
+ isAssigning,
250
+ components: import.meta.glob("./components/ui/*.tsx", {
251
+ eager: true,
252
+ }),
253
+ hooks: import.meta.glob("./hooks/*.{ts,tsx}", { eager: true }),
254
+ utils: import.meta.glob("./lib/*.{ts,tsx}", { eager: true }),
255
+ })}
246
256
  </div>
247
257
  )}
248
- {isAssigning && assignable && !assignable.isAvailable(record) && !checked && (
249
- <div>
250
- <div className="flex items-center justify-center space-x-3 min-h-8">
251
- {unavailable}
252
- </div>
258
+ {isAssigning && assignable && (assignable.isAvailable(record) || checked) && (
259
+ <div className="pb-4">
260
+ <div className="flex items-center justify-center space-x-3 min-h-8">
261
+ <Switch
262
+ id={`${record.id}-assigned`}
263
+ className="data-[state=checked]:bg-blue-500"
264
+ checked={checked}
265
+ disabled={checkedDisabled}
266
+ onCheckedChange={(checked) => handleCheckedChange(checked, record)}
267
+ />
268
+ {imagesConfig.size !== "sm" && (
269
+ <Label htmlFor={`${record.id}-assigned`}>Assigned</Label>
270
+ )}
253
271
  </div>
254
- )}
272
+ </div>
273
+ )}
274
+ {isAssigning && assignable && !assignable.isAvailable(record) && !checked && (
275
+ <div className="pb-4">
276
+ <div className="flex items-center justify-center space-x-3 min-h-8">
277
+ {unavailable}
278
+ </div>
279
+ </div>
280
+ )}
281
+ <div className={cn("grid", "gap-4", size)}>
255
282
  <button
256
283
  className="relative w-full h-full flex items-center justify-center overflow-hidden"
257
284
  onClick={() => goToRecord(collection, record)}
@@ -560,6 +587,10 @@ export const Images = memo(
560
587
  cols: "grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5",
561
588
  },
562
589
  lg: {
590
+ size: "h-[275px] md:h-[200px] lg:h-[250px] xl:h-[300px]",
591
+ cols: "grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3",
592
+ },
593
+ xl: {
563
594
  size: "h-[275px] md:h-[300px] lg:h-[400px] xl:h-[450px]",
564
595
  cols: "grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2",
565
596
  },
@@ -722,7 +753,13 @@ export const Images = memo(
722
753
 
723
754
  const lineClamp = imagesConfig.maxHeaderLines === 2 ? "line-clamp-2" : "line-clamp-1"
724
755
  const headerSize = imagesConfig.maxHeaderLines === 2 ? 116 : 82
725
- const assignedHeight = isAssigning ? 40 : 0
756
+ const assignedHeight = isAssigning ? 56 : 0
757
+ const customComponentHeight =
758
+ imagesConfig.customComponent &&
759
+ (!imagesConfig.customComponent.condition ||
760
+ imagesConfig.customComponent.condition(relationCollection, relationParent, isAssigning))
761
+ ? imagesConfig.customComponent.height
762
+ : 0
726
763
 
727
764
  const itemData = {
728
765
  collection,
@@ -768,7 +805,7 @@ export const Images = memo(
768
805
  <List
769
806
  height={height}
770
807
  width="100%"
771
- itemSize={itemSize + headerSize + assignedHeight}
808
+ itemSize={itemSize + headerSize + assignedHeight + customComponentHeight}
772
809
  itemCount={itemCount}
773
810
  overscanCount={5}
774
811
  itemKey={itemKey}
@@ -793,7 +830,7 @@ export const Images = memo(
793
830
  <List
794
831
  height={height}
795
832
  width="100%"
796
- itemSize={itemSize + headerSize + assignedHeight}
833
+ itemSize={itemSize + headerSize + assignedHeight + customComponentHeight}
797
834
  itemCount={itemCount}
798
835
  overscanCount={5}
799
836
  itemKey={itemKey}