@stoker-platform/web-app 0.5.133 → 0.5.135

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.
@@ -1834,595 +1834,635 @@ function Collection({
1834
1834
  relationList ? "xl:flex-row" : "lg:flex-row",
1835
1835
  )}
1836
1836
  >
1837
- {formList && (
1838
- <Badge variant="outline" className="py-2 px-4 text-md whitespace-nowrap">
1839
- {formList.label || collectionTitle || formList.collection}
1840
- </Badge>
1841
- )}
1842
- <div className="lg:h-9">
1843
- {!formList &&
1844
- (showList || showCards || showImages || showMap || showCalendar) && (
1845
- <TabsList>
1846
- {showList && (
1847
- <TabsTrigger value="list">
1848
- {listConfig?.title || "List"}
1849
- </TabsTrigger>
1850
- )}
1851
- {showCards && cardsStatusField.current && (
1852
- <TabsTrigger value="cards">
1853
- {cardsConfig?.title || "Board"}
1854
- </TabsTrigger>
1855
- )}
1856
- {showImages && (
1857
- <TabsTrigger value="images">
1858
- {imagesConfig?.title || "Pics"}
1859
- </TabsTrigger>
1860
- )}
1861
- {showMap && (
1862
- <TabsTrigger value="map">
1863
- {mapConfig?.title || "Map"}
1864
- </TabsTrigger>
1865
- )}
1866
- {showCalendar && (
1867
- <TabsTrigger value="calendar">
1868
- {calendarConfig?.title || "Calendar"}
1869
- </TabsTrigger>
1870
- )}
1871
- </TabsList>
1837
+ {isInitialized && (
1838
+ <>
1839
+ {formList && (
1840
+ <Badge
1841
+ variant="outline"
1842
+ className="py-2 px-4 text-md whitespace-nowrap"
1843
+ >
1844
+ {formList.label || collectionTitle || formList.collection}
1845
+ </Badge>
1872
1846
  )}
1873
- </div>
1874
- {!formList &&
1875
- !relationList?.loadAll &&
1876
- tab !== "calendar" &&
1877
- (hasRangeFilter || currentField) && (
1847
+ <div className="lg:h-9">
1848
+ {!formList &&
1849
+ (showList ||
1850
+ showCards ||
1851
+ showImages ||
1852
+ showMap ||
1853
+ showCalendar) && (
1854
+ <TabsList>
1855
+ {showList && (
1856
+ <TabsTrigger value="list">
1857
+ {listConfig?.title || "List"}
1858
+ </TabsTrigger>
1859
+ )}
1860
+ {showCards && cardsStatusField.current && (
1861
+ <TabsTrigger value="cards">
1862
+ {cardsConfig?.title || "Board"}
1863
+ </TabsTrigger>
1864
+ )}
1865
+ {showImages && (
1866
+ <TabsTrigger value="images">
1867
+ {imagesConfig?.title || "Pics"}
1868
+ </TabsTrigger>
1869
+ )}
1870
+ {showMap && (
1871
+ <TabsTrigger value="map">
1872
+ {mapConfig?.title || "Map"}
1873
+ </TabsTrigger>
1874
+ )}
1875
+ {showCalendar && (
1876
+ <TabsTrigger value="calendar">
1877
+ {calendarConfig?.title || "Calendar"}
1878
+ </TabsTrigger>
1879
+ )}
1880
+ </TabsList>
1881
+ )}
1882
+ </div>
1883
+ {!formList &&
1884
+ !relationList?.loadAll &&
1885
+ tab !== "calendar" &&
1886
+ (hasRangeFilter || currentField) && (
1887
+ <div
1888
+ className={cn(
1889
+ relationList
1890
+ ? "xl:hidden 2xl:flex xl:absolute xl:left-[calc(50%+128px)] xl:transform xl:-translate-x-[calc(50%+98px)] xl:mt-0 mt-2"
1891
+ : "lg:hidden 2xl:flex lg:absolute lg:left-1/2 lg:transform lg:-translate-x-1/2 lg:mt-0 mt-2",
1892
+ )}
1893
+ >
1894
+ <DateRangeSelector
1895
+ collection={collection}
1896
+ rangeSelector={rangeSelector}
1897
+ setRangeSelector={setRangeSelector}
1898
+ relationList={!!relationList}
1899
+ />
1900
+ </div>
1901
+ )}
1878
1902
  <div
1879
1903
  className={cn(
1904
+ "ml-auto flex items-center gap-2 justify-center w-full",
1880
1905
  relationList
1881
- ? "xl:hidden 2xl:flex xl:absolute xl:left-[calc(50%+128px)] xl:transform xl:-translate-x-[calc(50%+98px)] xl:mt-0 mt-2"
1882
- : "lg:hidden 2xl:flex lg:absolute lg:left-1/2 lg:transform lg:-translate-x-1/2 lg:mt-0 mt-2",
1906
+ ? "xl:justify-end xl:mt-0 mt-2"
1907
+ : "lg:justify-end lg:mt-0 mt-2",
1883
1908
  )}
1884
1909
  >
1885
- <DateRangeSelector
1886
- collection={collection}
1887
- rangeSelector={rangeSelector}
1888
- setRangeSelector={setRangeSelector}
1889
- relationList={!!relationList}
1890
- />
1891
- </div>
1892
- )}
1893
- <div
1894
- className={cn(
1895
- "ml-auto flex items-center gap-2 justify-center w-full",
1896
- relationList
1897
- ? "xl:justify-end xl:mt-0 mt-2"
1898
- : "lg:justify-end lg:mt-0 mt-2",
1899
- )}
1900
- >
1901
- {(statusField || softDeleteField) && (
1902
- <ToggleGroup
1903
- onValueChange={onStatusFilterChange}
1904
- value={statusFilter}
1905
- defaultValue="active"
1906
- size="sm"
1907
- type="single"
1908
- variant="outline"
1909
- className="text-muted-foreground font-medium mr-2 gap-2"
1910
- >
1911
- {statusField?.active &&
1912
- (tab !== "cards" || !autoUpdateStatusFilter) && (
1913
- <ToggleGroupItem
1914
- className="h-7 bg-muted data-[state=on]:bg-background"
1915
- value="active"
1916
- aria-label="Toggle active"
1917
- disabled={isRouteLoading.has(location.pathname)}
1918
- >
1919
- Active
1920
- </ToggleGroupItem>
1921
- )}
1922
- {statusField?.archived &&
1923
- (tab !== "cards" || !autoUpdateStatusFilter) && (
1910
+ {(statusField || softDeleteField) && (
1911
+ <ToggleGroup
1912
+ onValueChange={onStatusFilterChange}
1913
+ value={statusFilter}
1914
+ defaultValue="active"
1915
+ size="sm"
1916
+ type="single"
1917
+ variant="outline"
1918
+ className="text-muted-foreground font-medium mr-2 gap-2"
1919
+ >
1920
+ {statusField?.active &&
1921
+ (tab !== "cards" || !autoUpdateStatusFilter) && (
1922
+ <ToggleGroupItem
1923
+ className="h-7 bg-muted data-[state=on]:bg-background"
1924
+ value="active"
1925
+ aria-label="Toggle active"
1926
+ disabled={isRouteLoading.has(location.pathname)}
1927
+ >
1928
+ Active
1929
+ </ToggleGroupItem>
1930
+ )}
1931
+ {statusField?.archived &&
1932
+ (tab !== "cards" || !autoUpdateStatusFilter) && (
1933
+ <ToggleGroupItem
1934
+ className="hidden sm:flex h-7 bg-muted data-[state=on]:bg-background relative"
1935
+ value="archived"
1936
+ aria-label="Toggle archived"
1937
+ disabled={isRouteLoading.has(location.pathname)}
1938
+ >
1939
+ Archived
1940
+ {statusFilter === "archived" && (
1941
+ <span className="absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2 block h-3 w-3 rounded-full bg-destructive"></span>
1942
+ )}
1943
+ </ToggleGroupItem>
1944
+ )}
1924
1945
  <ToggleGroupItem
1925
- className="hidden sm:flex h-7 bg-muted data-[state=on]:bg-background relative"
1926
- value="archived"
1927
- aria-label="Toggle archived"
1946
+ className="h-7 bg-muted data-[state=on]:bg-background relative"
1947
+ value="all"
1948
+ aria-label="Toggle all"
1928
1949
  disabled={isRouteLoading.has(location.pathname)}
1929
1950
  >
1930
- Archived
1931
- {statusFilter === "archived" && (
1932
- <span className="absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2 block h-3 w-3 rounded-full bg-destructive"></span>
1933
- )}
1951
+ All
1952
+ {statusField &&
1953
+ statusFilter === "all" &&
1954
+ tab !== "cards" &&
1955
+ !revertingStatusFilter && (
1956
+ <span className="absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2 block h-3 w-3 rounded-full bg-destructive"></span>
1957
+ )}
1934
1958
  </ToggleGroupItem>
1935
- )}
1936
- <ToggleGroupItem
1937
- className="h-7 bg-muted data-[state=on]:bg-background relative"
1938
- value="all"
1939
- aria-label="Toggle all"
1940
- disabled={isRouteLoading.has(location.pathname)}
1941
- >
1942
- All
1943
- {statusField &&
1944
- statusFilter === "all" &&
1945
- tab !== "cards" &&
1946
- !revertingStatusFilter && (
1947
- <span className="absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2 block h-3 w-3 rounded-full bg-destructive"></span>
1959
+ {softDeleteField && (
1960
+ <ToggleGroupItem
1961
+ className="h-7 bg-muted data-[state=on]:bg-background relative"
1962
+ value="trash"
1963
+ aria-label="Toggle trash"
1964
+ disabled={isRouteLoading.has(location.pathname)}
1965
+ >
1966
+ Trash
1967
+ {statusFilter === "trash" && (
1968
+ <span className="absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2 block h-3 w-3 rounded-full bg-destructive"></span>
1969
+ )}
1970
+ </ToggleGroupItem>
1948
1971
  )}
1949
- </ToggleGroupItem>
1950
- {softDeleteField && (
1951
- <ToggleGroupItem
1952
- className="h-7 bg-muted data-[state=on]:bg-background relative"
1953
- value="trash"
1954
- aria-label="Toggle trash"
1955
- disabled={isRouteLoading.has(location.pathname)}
1956
- >
1957
- Trash
1958
- {statusFilter === "trash" && (
1959
- <span className="absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2 block h-3 w-3 rounded-full bg-destructive"></span>
1972
+ </ToggleGroup>
1973
+ )}
1974
+ {!formList && tab === "list" && (
1975
+ <>
1976
+ {customListActions &&
1977
+ customListActions.some(
1978
+ (action) => !action.condition || action.condition(),
1979
+ ) ? (
1980
+ <DropdownMenu>
1981
+ <DropdownMenuTrigger asChild>
1982
+ <Button
1983
+ type="button"
1984
+ size="sm"
1985
+ variant="outline"
1986
+ className="hidden sm:flex h-7 gap-1"
1987
+ >
1988
+ <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
1989
+ Actions
1990
+ </span>
1991
+ <ChevronsUpDown className="h-3.5 w-3.5" />
1992
+ </Button>
1993
+ </DropdownMenuTrigger>
1994
+ <DropdownMenuContent>
1995
+ {(!restrictExport ||
1996
+ restrictExport.includes(permissions.Role)) && (
1997
+ <DropdownMenuItem
1998
+ key="export"
1999
+ onClick={handleExport}
2000
+ disabled={
2001
+ !list.default?.length ||
2002
+ isRouteLoading.has(location.pathname)
2003
+ }
2004
+ >
2005
+ <File className="h-3.5 w-3.5 shrink-0 mr-1" />
2006
+ <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
2007
+ Export
2008
+ </span>
2009
+ </DropdownMenuItem>
2010
+ )}
2011
+ {customListActions
2012
+ .filter(
2013
+ (action) =>
2014
+ !action.condition || action.condition(),
2015
+ )
2016
+ .map((action) => (
2017
+ <DropdownMenuItem
2018
+ key={action.title}
2019
+ onClick={action.action}
2020
+ >
2021
+ {action.icon &&
2022
+ createElement(action.icon, {
2023
+ className:
2024
+ "h-3.5 w-3.5 shrink-0 mr-1",
2025
+ })}
2026
+ <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
2027
+ {action.title}
2028
+ </span>
2029
+ </DropdownMenuItem>
2030
+ ))}
2031
+ </DropdownMenuContent>
2032
+ </DropdownMenu>
2033
+ ) : (
2034
+ (!restrictExport ||
2035
+ restrictExport.includes(permissions.Role)) && (
2036
+ <Button
2037
+ type="button"
2038
+ size="sm"
2039
+ variant="outline"
2040
+ disabled={
2041
+ !list.default?.length ||
2042
+ isRouteLoading.has(location.pathname)
2043
+ }
2044
+ className="hidden sm:flex h-7 gap-1"
2045
+ onClick={handleExport}
2046
+ >
2047
+ <File className="h-3.5 w-3.5" />
2048
+ <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
2049
+ Export
2050
+ </span>
2051
+ </Button>
2052
+ )
1960
2053
  )}
1961
- </ToggleGroupItem>
2054
+ {(!restrictExport ||
2055
+ restrictExport.includes(permissions.Role)) && (
2056
+ <CSVLink
2057
+ ref={csvLinkRef}
2058
+ className="hidden"
2059
+ data={csvData?.data || []}
2060
+ headers={csvData?.headers || []}
2061
+ filename={`${collectionTitle}.csv`}
2062
+ target="_blank"
2063
+ />
2064
+ )}
2065
+ </>
1962
2066
  )}
1963
- </ToggleGroup>
1964
- )}
1965
- {!formList && tab === "list" && (
1966
- <>
1967
- {customListActions &&
1968
- customListActions.some(
1969
- (action) => !action.condition || action.condition(),
1970
- ) ? (
2067
+ {(tab === "cards" || tab === "images") && (
1971
2068
  <DropdownMenu>
1972
2069
  <DropdownMenuTrigger asChild>
1973
2070
  <Button
1974
2071
  type="button"
1975
2072
  size="sm"
1976
2073
  variant="outline"
1977
- className="hidden sm:flex h-7 gap-1"
2074
+ className="h-7 gap-1"
2075
+ disabled={isRouteLoading.has(location.pathname)}
1978
2076
  >
2077
+ <ChevronsUpDown className="h-3.5 w-3.5" />
1979
2078
  <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
1980
- Actions
2079
+ Sort
1981
2080
  </span>
1982
- <ChevronsUpDown className="h-3.5 w-3.5" />
1983
2081
  </Button>
1984
2082
  </DropdownMenuTrigger>
1985
2083
  <DropdownMenuContent>
1986
- {(!restrictExport ||
1987
- restrictExport.includes(permissions.Role)) && (
1988
- <DropdownMenuItem
1989
- key="export"
1990
- onClick={handleExport}
1991
- disabled={
1992
- !list.default?.length ||
1993
- isRouteLoading.has(location.pathname)
1994
- }
1995
- >
1996
- <File className="h-3.5 w-3.5 shrink-0 mr-1" />
1997
- <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
1998
- Export
1999
- </span>
2000
- </DropdownMenuItem>
2001
- )}
2002
- {customListActions
2003
- .filter(
2004
- (action) => !action.condition || action.condition(),
2005
- )
2006
- .map((action) => (
2007
- <DropdownMenuItem
2008
- key={action.title}
2009
- onClick={action.action}
2010
- >
2011
- {action.icon &&
2012
- createElement(action.icon, {
2013
- className: "h-3.5 w-3.5 shrink-0 mr-1",
2014
- })}
2015
- <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
2016
- {action.title}
2017
- </span>
2018
- </DropdownMenuItem>
2019
- ))}
2084
+ <ScrollArea className={sortingHeight}>
2085
+ <div>
2086
+ {sortingFields.map((field: CollectionField) => {
2087
+ const fieldCustomization =
2088
+ getFieldCustomization(field, customization)
2089
+ const label =
2090
+ tryFunction(
2091
+ fieldCustomization.admin?.label,
2092
+ ) || field.name
2093
+ const condition =
2094
+ fieldCustomization.admin?.condition?.list
2095
+ if (
2096
+ condition !== undefined &&
2097
+ !tryFunction(condition)
2098
+ )
2099
+ return null
2100
+ return (
2101
+ <DropdownMenuItem
2102
+ key={field.name}
2103
+ onClick={() => {
2104
+ if (preventChange) return
2105
+ if (
2106
+ typeof field.sorting ===
2107
+ "object" &&
2108
+ field.sorting.direction ===
2109
+ "desc"
2110
+ ) {
2111
+ setOrder({
2112
+ field: field.name,
2113
+ direction: "desc",
2114
+ })
2115
+ } else {
2116
+ setOrder({
2117
+ field: field.name,
2118
+ direction: "asc",
2119
+ })
2120
+ }
2121
+ setState(
2122
+ `collection-sort-${labels.collection.toLowerCase()}`,
2123
+ "sort",
2124
+ JSON.stringify([
2125
+ {
2126
+ id: field.name,
2127
+ desc:
2128
+ typeof field.sorting ===
2129
+ "object" &&
2130
+ field.sorting
2131
+ .direction ===
2132
+ "desc",
2133
+ },
2134
+ ]),
2135
+ )
2136
+ }}
2137
+ >
2138
+ {order?.field === field.name && (
2139
+ <Check className="absolute h-3.5 w-3.5 mr-1" />
2140
+ )}
2141
+ <span className="ml-5">{label}</span>
2142
+ </DropdownMenuItem>
2143
+ )
2144
+ })}
2145
+ </div>
2146
+ </ScrollArea>
2020
2147
  </DropdownMenuContent>
2021
2148
  </DropdownMenu>
2022
- ) : (
2023
- (!restrictExport || restrictExport.includes(permissions.Role)) && (
2024
- <Button
2025
- type="button"
2026
- size="sm"
2027
- variant="outline"
2028
- disabled={
2029
- !list.default?.length ||
2030
- isRouteLoading.has(location.pathname)
2031
- }
2032
- className="hidden sm:flex h-7 gap-1"
2033
- onClick={handleExport}
2034
- >
2035
- <File className="h-3.5 w-3.5" />
2036
- <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
2037
- Export
2038
- </span>
2039
- </Button>
2040
- )
2041
2149
  )}
2042
- {(!restrictExport || restrictExport.includes(permissions.Role)) && (
2043
- <CSVLink
2044
- ref={csvLinkRef}
2045
- className="hidden"
2046
- data={csvData?.data || []}
2047
- headers={csvData?.headers || []}
2048
- filename={`${collectionTitle}.csv`}
2049
- target="_blank"
2050
- />
2150
+ {hasFiltersToShow && (
2151
+ <Sheet>
2152
+ <SheetTrigger asChild>
2153
+ <Button
2154
+ type="button"
2155
+ variant="outline"
2156
+ size="sm"
2157
+ className="h-7 gap-1 relative"
2158
+ >
2159
+ <ListFilter className="h-3.5 w-3.5" />
2160
+ <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
2161
+ Filter
2162
+ </span>
2163
+ {filtersActive && (
2164
+ <span className="absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2 block h-3 w-3 rounded-full bg-destructive"></span>
2165
+ )}
2166
+ </Button>
2167
+ </SheetTrigger>
2168
+ <SheetContent className="overflow-y-auto">
2169
+ <SheetHeader>
2170
+ <SheetTitle className="mb-4">Filters</SheetTitle>
2171
+ <SheetDescription className="hidden">
2172
+ Filter records in the list view.
2173
+ </SheetDescription>
2174
+ </SheetHeader>
2175
+ <Filters
2176
+ collection={collection}
2177
+ excluded={excludedFilters}
2178
+ relationList={relationList}
2179
+ />
2180
+ </SheetContent>
2181
+ </Sheet>
2051
2182
  )}
2052
- </>
2053
- )}
2054
- {(tab === "cards" || tab === "images") && (
2055
- <DropdownMenu>
2056
- <DropdownMenuTrigger asChild>
2057
- <Button
2058
- type="button"
2059
- size="sm"
2060
- variant="outline"
2061
- className="h-7 gap-1"
2062
- disabled={isRouteLoading.has(location.pathname)}
2063
- >
2064
- <ChevronsUpDown className="h-3.5 w-3.5" />
2065
- <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
2066
- Sort
2067
- </span>
2068
- </Button>
2069
- </DropdownMenuTrigger>
2070
- <DropdownMenuContent>
2071
- <ScrollArea className={sortingHeight}>
2072
- <div>
2073
- {sortingFields.map((field: CollectionField) => {
2074
- const fieldCustomization = getFieldCustomization(
2075
- field,
2076
- customization,
2077
- )
2078
- const label =
2079
- tryFunction(fieldCustomization.admin?.label) ||
2080
- field.name
2081
- const condition =
2082
- fieldCustomization.admin?.condition?.list
2083
- if (condition !== undefined && !tryFunction(condition))
2084
- return null
2183
+ {canAddRecords && (
2184
+ <>
2185
+ {(() => {
2186
+ const relationFieldSchema = relationList
2187
+ ? getField(fields, relationList.field)
2188
+ : undefined
2189
+ const isManyToMany =
2190
+ relationFieldSchema &&
2191
+ relationFieldSchema.type === "ManyToMany"
2192
+ if (relationList && isManyToMany) {
2085
2193
  return (
2086
- <DropdownMenuItem
2087
- key={field.name}
2088
- onClick={() => {
2089
- if (preventChange) return
2090
- if (
2091
- typeof field.sorting === "object" &&
2092
- field.sorting.direction === "desc"
2093
- ) {
2094
- setOrder({
2095
- field: field.name,
2096
- direction: "desc",
2097
- })
2098
- } else {
2099
- setOrder({
2100
- field: field.name,
2101
- direction: "asc",
2102
- })
2103
- }
2104
- setState(
2105
- `collection-sort-${labels.collection.toLowerCase()}`,
2106
- "sort",
2107
- JSON.stringify([
2108
- {
2109
- id: field.name,
2110
- desc:
2111
- typeof field.sorting ===
2112
- "object" &&
2113
- field.sorting.direction ===
2114
- "desc",
2115
- },
2116
- ]),
2117
- )
2118
- }}
2119
- >
2120
- {order?.field === field.name && (
2121
- <Check className="absolute h-3.5 w-3.5 mr-1" />
2122
- )}
2123
- <span className="ml-5">{label}</span>
2124
- </DropdownMenuItem>
2125
- )
2126
- })}
2127
- </div>
2128
- </ScrollArea>
2129
- </DropdownMenuContent>
2130
- </DropdownMenu>
2131
- )}
2132
- {hasFiltersToShow && (
2133
- <Sheet>
2134
- <SheetTrigger asChild>
2135
- <Button
2136
- type="button"
2137
- variant="outline"
2138
- size="sm"
2139
- className="h-7 gap-1 relative"
2140
- >
2141
- <ListFilter className="h-3.5 w-3.5" />
2142
- <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
2143
- Filter
2144
- </span>
2145
- {filtersActive && (
2146
- <span className="absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2 block h-3 w-3 rounded-full bg-destructive"></span>
2147
- )}
2148
- </Button>
2149
- </SheetTrigger>
2150
- <SheetContent className="overflow-y-auto">
2151
- <SheetHeader>
2152
- <SheetTitle className="mb-4">Filters</SheetTitle>
2153
- <SheetDescription className="hidden">
2154
- Filter records in the list view.
2155
- </SheetDescription>
2156
- </SheetHeader>
2157
- <Filters
2158
- collection={collection}
2159
- excluded={excludedFilters}
2160
- relationList={relationList}
2161
- />
2162
- </SheetContent>
2163
- </Sheet>
2164
- )}
2165
- {canAddRecords && (
2166
- <>
2167
- {(() => {
2168
- const relationFieldSchema = relationList
2169
- ? getField(fields, relationList.field)
2170
- : undefined
2171
- const isManyToMany =
2172
- relationFieldSchema && relationFieldSchema.type === "ManyToMany"
2173
- if (relationList && isManyToMany) {
2174
- return (
2175
- <DropdownMenu>
2176
- <DropdownMenuTrigger asChild>
2177
- <Button
2178
- type="button"
2179
- ref={addButtonRef}
2180
- size="sm"
2181
- className="h-7 gap-1"
2182
- disabled={isCreateDisabled}
2183
- >
2184
- <PlusCircle className="h-3.5 w-3.5" />
2185
- <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
2186
- Add {recordTitle}
2187
- </span>
2188
- </Button>
2189
- </DropdownMenuTrigger>
2190
- <DropdownMenuContent align="end">
2191
- <DropdownMenuItem
2192
- onClick={() => {
2193
- ;(async () => {
2194
- const customization =
2195
- getCollectionConfigModule(
2196
- labels.collection,
2197
- )
2198
- const override =
2199
- customization.admin
2200
- ?.addRecordButtonOverride
2201
- if (
2202
- override &&
2203
- typeof override === "function"
2204
- ) {
2205
- await tryPromise(() =>
2206
- override(
2207
- createPrePopulatedRecord(),
2208
- ),
2209
- )
2210
- return
2211
- }
2212
- setSelectedDateRange(null)
2213
- setIsCreateDialogOpen(true)
2214
- })()
2215
- }}
2216
- >
2217
- Add new
2218
- </DropdownMenuItem>
2219
- <DropdownMenuItem
2220
- onClick={() => {
2221
- setShowSelectExisting(true)
2222
- fetchSelectableRecords()
2223
- }}
2224
- >
2225
- Select existing
2226
- </DropdownMenuItem>
2227
- </DropdownMenuContent>
2228
- </DropdownMenu>
2229
- )
2230
- }
2231
- return (
2232
- <Button
2233
- type="button"
2234
- ref={addButtonRef}
2235
- size="sm"
2236
- className="h-7 gap-1"
2237
- disabled={isCreateDisabled}
2238
- onClick={() => {
2239
- ;(async () => {
2240
- const customization = getCollectionConfigModule(
2241
- labels.collection,
2242
- )
2243
- const override =
2244
- customization.admin?.addRecordButtonOverride
2245
- if (override && typeof override === "function") {
2246
- await tryPromise(() =>
2247
- override(createPrePopulatedRecord()),
2248
- )
2249
- return
2250
- }
2251
- setSelectedDateRange(null)
2252
- setIsCreateDialogOpen(true)
2253
- })()
2254
- }}
2255
- >
2256
- <PlusCircle className="h-3.5 w-3.5" />
2257
- <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
2258
- Add {recordTitle}
2259
- </span>
2260
- </Button>
2261
- )
2262
- })()}
2263
- {isCreateDialogOpen &&
2264
- createPortal(
2265
- <div
2266
- id="create-record-modal"
2267
- className="fixed inset-0 z-50 flex items-center justify-center animate-in fade-in slide-in-from-top-4 duration-300"
2268
- aria-modal="true"
2269
- aria-live="polite"
2270
- role="dialog"
2271
- >
2272
- <div className="fixed inset-0 bg-black/50" />
2273
- <div
2274
- className="relative bg-background sm:rounded-lg w-full max-w-2xl h-full sm:h-[90vh] overflow-hidden border border-border"
2275
- aria-labelledby="dialog-title"
2276
- >
2277
- <div className="h-full overflow-y-auto overscroll-contain p-6">
2278
- <div className="space-y-2">
2279
- <div className="flex justify-between items-center mb-4">
2280
- <h4
2281
- id="dialog-title"
2282
- className="font-medium leading-none"
2283
- >
2284
- Add {recordTitle}
2285
- </h4>
2194
+ <DropdownMenu>
2195
+ <DropdownMenuTrigger asChild>
2286
2196
  <Button
2287
2197
  type="button"
2288
- variant="ghost"
2289
- size="icon"
2290
- className="right-4 top-4"
2198
+ ref={addButtonRef}
2199
+ size="sm"
2200
+ className="h-7 gap-1"
2201
+ disabled={isCreateDisabled}
2202
+ >
2203
+ <PlusCircle className="h-3.5 w-3.5" />
2204
+ <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
2205
+ Add {recordTitle}
2206
+ </span>
2207
+ </Button>
2208
+ </DropdownMenuTrigger>
2209
+ <DropdownMenuContent align="end">
2210
+ <DropdownMenuItem
2291
2211
  onClick={() => {
2292
- setIsCreateDialogOpen(false)
2293
- setSelectedDateRange(null)
2294
- setTimeout(() => {
2295
- addButtonRef.current?.focus()
2296
- }, 0)
2297
-
2298
- localStorage.removeItem(
2299
- `stoker-draft-${labels.collection}`,
2300
- )
2212
+ ;(async () => {
2213
+ const customization =
2214
+ getCollectionConfigModule(
2215
+ labels.collection,
2216
+ )
2217
+ const override =
2218
+ customization.admin
2219
+ ?.addRecordButtonOverride
2220
+ if (
2221
+ override &&
2222
+ typeof override ===
2223
+ "function"
2224
+ ) {
2225
+ await tryPromise(() =>
2226
+ override(
2227
+ createPrePopulatedRecord(),
2228
+ ),
2229
+ )
2230
+ return
2231
+ }
2232
+ setSelectedDateRange(null)
2233
+ setIsCreateDialogOpen(true)
2234
+ })()
2301
2235
  }}
2302
2236
  >
2303
- <X className="h-4 w-4" />
2304
- <span className="sr-only">Close</span>
2305
- </Button>
2306
- </div>
2307
- <RecordForm
2308
- collection={collection}
2309
- operation="create"
2310
- path={[labels.collection]}
2311
- record={createPrePopulatedRecord()}
2312
- draft={true}
2313
- parentCollection={
2314
- relationCollection?.labels.collection
2237
+ Add new
2238
+ </DropdownMenuItem>
2239
+ <DropdownMenuItem
2240
+ onClick={() => {
2241
+ setShowSelectExisting(true)
2242
+ fetchSelectableRecords()
2243
+ }}
2244
+ >
2245
+ Select existing
2246
+ </DropdownMenuItem>
2247
+ </DropdownMenuContent>
2248
+ </DropdownMenu>
2249
+ )
2250
+ }
2251
+ return (
2252
+ <Button
2253
+ type="button"
2254
+ ref={addButtonRef}
2255
+ size="sm"
2256
+ className="h-7 gap-1"
2257
+ disabled={isCreateDisabled}
2258
+ onClick={() => {
2259
+ ;(async () => {
2260
+ const customization =
2261
+ getCollectionConfigModule(
2262
+ labels.collection,
2263
+ )
2264
+ const override =
2265
+ customization.admin
2266
+ ?.addRecordButtonOverride
2267
+ if (
2268
+ override &&
2269
+ typeof override === "function"
2270
+ ) {
2271
+ await tryPromise(() =>
2272
+ override(
2273
+ createPrePopulatedRecord(),
2274
+ ),
2275
+ )
2276
+ return
2315
2277
  }
2316
- parentRecord={relationParent}
2317
- onSuccess={() => {
2318
- setIsCreateDialogOpen(false)
2319
- setSelectedDateRange(null)
2320
- setTimeout(() => {
2321
- addButtonRef.current?.focus()
2322
- }, 0)
2323
- if (isServerReadOnly) {
2324
- setBackToStartKey(
2325
- (prev) => prev + 1,
2326
- )
2327
- }
2328
- }}
2329
- />
2330
- </div>
2331
- </div>
2332
- </div>
2333
- </div>,
2334
- document.body,
2335
- )}
2336
- {showSelectExisting &&
2337
- relationList &&
2338
- relationParent &&
2339
- createPortal(
2340
- <div
2341
- id="select-existing-modal"
2342
- className="fixed inset-0 z-50 flex items-center justify-center animate-in fade-in slide-in-from-top-4 duration-300"
2343
- aria-modal="true"
2344
- aria-live="polite"
2345
- role="dialog"
2346
- >
2347
- <div className="fixed inset-0 bg-black/50" />
2348
- <div className="relative bg-background sm:rounded-lg w-full max-w-2xl h-full sm:h-[50vh] overflow-hidden border border-border">
2349
- <div className="h-full overflow-y-auto overscroll-contain p-6">
2350
- <div className="flex items-center justify-between mb-4">
2351
- <h4 className="font-medium leading-none">
2352
- Select {recordTitle}
2353
- </h4>
2354
- <Button
2355
- type="button"
2356
- variant="ghost"
2357
- size="icon"
2358
- onClick={() => {
2359
- setShowSelectExisting(false)
2360
- setSelectableSearch("")
2361
- setSelectableData([])
2362
- }}
2363
- >
2364
- <X className="h-4 w-4" />
2365
- <span className="sr-only">Close</span>
2366
- </Button>
2278
+ setSelectedDateRange(null)
2279
+ setIsCreateDialogOpen(true)
2280
+ })()
2281
+ }}
2282
+ >
2283
+ <PlusCircle className="h-3.5 w-3.5" />
2284
+ <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
2285
+ Add {recordTitle}
2286
+ </span>
2287
+ </Button>
2288
+ )
2289
+ })()}
2290
+ {isCreateDialogOpen &&
2291
+ createPortal(
2292
+ <div
2293
+ id="create-record-modal"
2294
+ className="fixed inset-0 z-50 flex items-center justify-center animate-in fade-in slide-in-from-top-4 duration-300"
2295
+ aria-modal="true"
2296
+ aria-live="polite"
2297
+ role="dialog"
2298
+ >
2299
+ <div className="fixed inset-0 bg-black/50" />
2300
+ <div
2301
+ className="relative bg-background sm:rounded-lg w-full max-w-2xl h-full sm:h-[90vh] overflow-hidden border border-border"
2302
+ aria-labelledby="dialog-title"
2303
+ >
2304
+ <div className="h-full overflow-y-auto overscroll-contain p-6">
2305
+ <div className="space-y-2">
2306
+ <div className="flex justify-between items-center mb-4">
2307
+ <h4
2308
+ id="dialog-title"
2309
+ className="font-medium leading-none"
2310
+ >
2311
+ Add {recordTitle}
2312
+ </h4>
2313
+ <Button
2314
+ type="button"
2315
+ variant="ghost"
2316
+ size="icon"
2317
+ className="right-4 top-4"
2318
+ onClick={() => {
2319
+ setIsCreateDialogOpen(false)
2320
+ setSelectedDateRange(null)
2321
+ setTimeout(() => {
2322
+ addButtonRef.current?.focus()
2323
+ }, 0)
2324
+
2325
+ localStorage.removeItem(
2326
+ `stoker-draft-${labels.collection}`,
2327
+ )
2328
+ }}
2329
+ >
2330
+ <X className="h-4 w-4" />
2331
+ <span className="sr-only">
2332
+ Close
2333
+ </span>
2334
+ </Button>
2335
+ </div>
2336
+ <RecordForm
2337
+ collection={collection}
2338
+ operation="create"
2339
+ path={[labels.collection]}
2340
+ record={createPrePopulatedRecord()}
2341
+ draft={true}
2342
+ parentCollection={
2343
+ relationCollection?.labels
2344
+ .collection
2345
+ }
2346
+ parentRecord={relationParent}
2347
+ onSuccess={() => {
2348
+ setIsCreateDialogOpen(false)
2349
+ setSelectedDateRange(null)
2350
+ setTimeout(() => {
2351
+ addButtonRef.current?.focus()
2352
+ }, 0)
2353
+ if (isServerReadOnly) {
2354
+ setBackToStartKey(
2355
+ (prev) => prev + 1,
2356
+ )
2357
+ }
2358
+ }}
2359
+ />
2360
+ </div>
2361
+ </div>
2367
2362
  </div>
2368
- <div>
2369
- <Command filter={() => 1}>
2370
- <CommandInput
2371
- placeholder={`Search ${collectionTitle}...`}
2372
- className="h-9"
2373
- value={selectableSearch}
2374
- onValueChange={(value) => {
2375
- setSelectableSearch(value)
2376
- fetchSelectableRecords(value)
2377
- }}
2378
- />
2379
- <CommandList className="max-h-full sm:max-h-[calc(50vh-138px)]">
2380
- <CommandEmpty>
2381
- {selectLoading ? (
2382
- <LoadingSpinner
2383
- size={7}
2384
- className="m-auto"
2385
- />
2386
- ) : !selectLoadingImmediate ? (
2387
- `No ${collectionTitle} found.`
2388
- ) : null}
2389
- </CommandEmpty>
2390
- {(!selectLoading ||
2391
- isPreloadCacheEnabled) && (
2392
- <CommandGroup>
2393
- {selectableData.map(
2394
- (record) => (
2395
- <CommandItem
2396
- key={record.id}
2397
- value={record.id}
2398
- onSelect={() => {
2399
- linkExistingRecord(
2400
- record,
2401
- )
2402
- }}
2403
- >
2404
- {
2405
- record[
2406
- recordTitleField ||
2407
- "id"
2408
- ]
2409
- }
2410
- </CommandItem>
2411
- ),
2363
+ </div>,
2364
+ document.body,
2365
+ )}
2366
+ {showSelectExisting &&
2367
+ relationList &&
2368
+ relationParent &&
2369
+ createPortal(
2370
+ <div
2371
+ id="select-existing-modal"
2372
+ className="fixed inset-0 z-50 flex items-center justify-center animate-in fade-in slide-in-from-top-4 duration-300"
2373
+ aria-modal="true"
2374
+ aria-live="polite"
2375
+ role="dialog"
2376
+ >
2377
+ <div className="fixed inset-0 bg-black/50" />
2378
+ <div className="relative bg-background sm:rounded-lg w-full max-w-2xl h-full sm:h-[50vh] overflow-hidden border border-border">
2379
+ <div className="h-full overflow-y-auto overscroll-contain p-6">
2380
+ <div className="flex items-center justify-between mb-4">
2381
+ <h4 className="font-medium leading-none">
2382
+ Select {recordTitle}
2383
+ </h4>
2384
+ <Button
2385
+ type="button"
2386
+ variant="ghost"
2387
+ size="icon"
2388
+ onClick={() => {
2389
+ setShowSelectExisting(false)
2390
+ setSelectableSearch("")
2391
+ setSelectableData([])
2392
+ }}
2393
+ >
2394
+ <X className="h-4 w-4" />
2395
+ <span className="sr-only">
2396
+ Close
2397
+ </span>
2398
+ </Button>
2399
+ </div>
2400
+ <div>
2401
+ <Command filter={() => 1}>
2402
+ <CommandInput
2403
+ placeholder={`Search ${collectionTitle}...`}
2404
+ className="h-9"
2405
+ value={selectableSearch}
2406
+ onValueChange={(value) => {
2407
+ setSelectableSearch(value)
2408
+ fetchSelectableRecords(
2409
+ value,
2410
+ )
2411
+ }}
2412
+ />
2413
+ <CommandList className="max-h-full sm:max-h-[calc(50vh-138px)]">
2414
+ <CommandEmpty>
2415
+ {selectLoading ? (
2416
+ <LoadingSpinner
2417
+ size={7}
2418
+ className="m-auto"
2419
+ />
2420
+ ) : !selectLoadingImmediate ? (
2421
+ `No ${collectionTitle} found.`
2422
+ ) : null}
2423
+ </CommandEmpty>
2424
+ {(!selectLoading ||
2425
+ isPreloadCacheEnabled) && (
2426
+ <CommandGroup>
2427
+ {selectableData.map(
2428
+ (record) => (
2429
+ <CommandItem
2430
+ key={
2431
+ record.id
2432
+ }
2433
+ value={
2434
+ record.id
2435
+ }
2436
+ onSelect={() => {
2437
+ linkExistingRecord(
2438
+ record,
2439
+ )
2440
+ }}
2441
+ >
2442
+ {
2443
+ record[
2444
+ recordTitleField ||
2445
+ "id"
2446
+ ]
2447
+ }
2448
+ </CommandItem>
2449
+ ),
2450
+ )}
2451
+ </CommandGroup>
2412
2452
  )}
2413
- </CommandGroup>
2414
- )}
2415
- </CommandList>
2416
- </Command>
2453
+ </CommandList>
2454
+ </Command>
2455
+ </div>
2456
+ </div>
2417
2457
  </div>
2418
- </div>
2419
- </div>
2420
- </div>,
2421
- document.body,
2422
- )}
2423
- </>
2424
- )}
2425
- </div>
2458
+ </div>,
2459
+ document.body,
2460
+ )}
2461
+ </>
2462
+ )}
2463
+ </div>
2464
+ </>
2465
+ )}
2426
2466
  </div>
2427
2467
  {tab && isInitialized ? (
2428
2468
  <>