@carlonicora/nextjs-jsonapi 1.24.3 → 1.25.1

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.
Files changed (76) hide show
  1. package/dist/{BlockNoteEditor-OFSTXGZX.js → BlockNoteEditor-7WAWEZVW.js} +13 -13
  2. package/dist/{BlockNoteEditor-OFSTXGZX.js.map → BlockNoteEditor-7WAWEZVW.js.map} +1 -1
  3. package/dist/{BlockNoteEditor-TJNLCNIP.mjs → BlockNoteEditor-UNVKGZ2G.mjs} +3 -3
  4. package/dist/billing/index.js +342 -342
  5. package/dist/billing/index.mjs +2 -2
  6. package/dist/{chunk-H5JZ5E7M.mjs → chunk-6BDOZDZ3.mjs} +1247 -54
  7. package/dist/chunk-6BDOZDZ3.mjs.map +1 -0
  8. package/dist/{chunk-EJALOG7L.js → chunk-JI6BDV7L.js} +1598 -405
  9. package/dist/chunk-JI6BDV7L.js.map +1 -0
  10. package/dist/{chunk-5U4NJJOF.mjs → chunk-LNBT2YPZ.mjs} +289 -2
  11. package/dist/chunk-LNBT2YPZ.mjs.map +1 -0
  12. package/dist/{chunk-NQVPCNRS.js → chunk-O3LLMGP7.js} +290 -3
  13. package/dist/chunk-O3LLMGP7.js.map +1 -0
  14. package/dist/client/index.d.mts +96 -1
  15. package/dist/client/index.d.ts +96 -1
  16. package/dist/client/index.js +9 -3
  17. package/dist/client/index.js.map +1 -1
  18. package/dist/client/index.mjs +8 -2
  19. package/dist/components/index.d.mts +225 -1
  20. package/dist/components/index.d.ts +225 -1
  21. package/dist/components/index.js +25 -3
  22. package/dist/components/index.js.map +1 -1
  23. package/dist/components/index.mjs +24 -2
  24. package/dist/contexts/index.js +3 -3
  25. package/dist/contexts/index.mjs +2 -2
  26. package/dist/core/index.d.mts +108 -1
  27. package/dist/core/index.d.ts +108 -1
  28. package/dist/core/index.js +14 -2
  29. package/dist/core/index.js.map +1 -1
  30. package/dist/core/index.mjs +13 -1
  31. package/dist/index.d.mts +2 -1
  32. package/dist/index.d.ts +2 -1
  33. package/dist/index.js +14 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/index.mjs +13 -1
  36. package/dist/oauth.interface-DsZ5ecSX.d.mts +119 -0
  37. package/dist/oauth.interface-vL7za9Bz.d.ts +119 -0
  38. package/dist/server/index.js +3 -3
  39. package/dist/server/index.mjs +1 -1
  40. package/package.json +3 -2
  41. package/src/client/index.ts +1 -0
  42. package/src/components/index.ts +1 -0
  43. package/src/core/index.ts +3 -0
  44. package/src/core/registry/ModuleRegistry.ts +2 -0
  45. package/src/features/index.ts +1 -0
  46. package/src/features/oauth/atoms/index.ts +1 -0
  47. package/src/features/oauth/atoms/oauth.atoms.ts +131 -0
  48. package/src/features/oauth/components/OAuthClientCard.tsx +105 -0
  49. package/src/features/oauth/components/OAuthClientDetail.tsx +269 -0
  50. package/src/features/oauth/components/OAuthClientForm.tsx +212 -0
  51. package/src/features/oauth/components/OAuthClientList.tsx +127 -0
  52. package/src/features/oauth/components/OAuthClientSecretDisplay.tsx +127 -0
  53. package/src/features/oauth/components/OAuthRedirectUriInput.tsx +152 -0
  54. package/src/features/oauth/components/OAuthScopeSelector.tsx +123 -0
  55. package/src/features/oauth/components/consent/OAuthConsentActions.tsx +41 -0
  56. package/src/features/oauth/components/consent/OAuthConsentHeader.tsx +51 -0
  57. package/src/features/oauth/components/consent/OAuthConsentScreen.tsx +142 -0
  58. package/src/features/oauth/components/consent/OAuthScopeList.tsx +72 -0
  59. package/src/features/oauth/components/consent/index.ts +4 -0
  60. package/src/features/oauth/components/index.ts +8 -0
  61. package/src/features/oauth/data/index.ts +2 -0
  62. package/src/features/oauth/data/oauth.service.ts +191 -0
  63. package/src/features/oauth/data/oauth.ts +87 -0
  64. package/src/features/oauth/hooks/index.ts +3 -0
  65. package/src/features/oauth/hooks/useOAuthClient.ts +161 -0
  66. package/src/features/oauth/hooks/useOAuthClients.ts +111 -0
  67. package/src/features/oauth/hooks/useOAuthConsent.ts +125 -0
  68. package/src/features/oauth/index.ts +6 -0
  69. package/src/features/oauth/interfaces/index.ts +1 -0
  70. package/src/features/oauth/interfaces/oauth.interface.ts +175 -0
  71. package/src/features/oauth/oauth.module.ts +9 -0
  72. package/dist/chunk-5U4NJJOF.mjs.map +0 -1
  73. package/dist/chunk-EJALOG7L.js.map +0 -1
  74. package/dist/chunk-H5JZ5E7M.mjs.map +0 -1
  75. package/dist/chunk-NQVPCNRS.js.map +0 -1
  76. /package/dist/{BlockNoteEditor-TJNLCNIP.mjs.map → BlockNoteEditor-UNVKGZ2G.mjs.map} +0 -0
@@ -13,13 +13,17 @@ import {
13
13
  useI18nTranslations
14
14
  } from "./chunk-GR4QPP36.mjs";
15
15
  import {
16
+ AVAILABLE_OAUTH_SCOPES,
16
17
  AuthService,
17
18
  ClientAbstractService,
18
19
  CompanyService,
19
20
  ContentService,
21
+ DEFAULT_GRANT_TYPES,
20
22
  FeatureService,
21
23
  Modules,
22
24
  NotificationService,
25
+ OAUTH_SCOPE_DISPLAY,
26
+ OAuthService,
23
27
  PushService,
24
28
  RehydrationFactory,
25
29
  RoleService,
@@ -32,7 +36,7 @@ import {
32
36
  rehydrate,
33
37
  useComposedRefs,
34
38
  useIsMobile
35
- } from "./chunk-5U4NJJOF.mjs";
39
+ } from "./chunk-LNBT2YPZ.mjs";
36
40
  import {
37
41
  JsonApiContext
38
42
  } from "./chunk-VOXD3ZLY.mjs";
@@ -10962,7 +10966,7 @@ __name(AllowedUsersDetails, "AllowedUsersDetails");
10962
10966
  import dynamic from "next/dynamic";
10963
10967
  import React15 from "react";
10964
10968
  import { jsx as jsx121 } from "react/jsx-runtime";
10965
- var BlockNoteEditor = dynamic(() => import("./BlockNoteEditor-TJNLCNIP.mjs"), {
10969
+ var BlockNoteEditor = dynamic(() => import("./BlockNoteEditor-UNVKGZ2G.mjs"), {
10966
10970
  ssr: false
10967
10971
  });
10968
10972
  var BlockNoteEditorContainer = React15.memo(/* @__PURE__ */ __name(function EditorContainer(props) {
@@ -11717,6 +11721,294 @@ var useContentTableStructure = /* @__PURE__ */ __name((params) => {
11717
11721
  return useMemo17(() => ({ data: tableData, columns }), [tableData, columns]);
11718
11722
  }, "useContentTableStructure");
11719
11723
 
11724
+ // src/features/oauth/hooks/useOAuthClients.ts
11725
+ import { useAtom as useAtom3, useSetAtom } from "jotai";
11726
+ import { useCallback as useCallback16, useEffect as useEffect34 } from "react";
11727
+
11728
+ // src/features/oauth/atoms/oauth.atoms.ts
11729
+ import { atom } from "jotai";
11730
+ import { atomFamily } from "jotai-family";
11731
+ var oauthClientsAtom = atom([]);
11732
+ var oauthClientsLoadingAtom = atom(false);
11733
+ var oauthClientsErrorAtom = atom(null);
11734
+ var oauthClientByIdAtom = atomFamily(
11735
+ (clientId) => atom((get) => {
11736
+ const clients = get(oauthClientsAtom);
11737
+ return clients.find((c) => c.clientId === clientId || c.id === clientId);
11738
+ })
11739
+ );
11740
+ var oauthNewClientSecretAtom = atom(null);
11741
+ var oauthNewClientIdAtom = atom(null);
11742
+ var oauthConsentLoadingAtom = atom(false);
11743
+ var oauthConsentErrorAtom = atom(null);
11744
+ var setNewClientSecretAtom = atom(null, (get, set, value) => {
11745
+ if (value === null) {
11746
+ set(oauthNewClientSecretAtom, null);
11747
+ set(oauthNewClientIdAtom, null);
11748
+ } else {
11749
+ set(oauthNewClientSecretAtom, value.secret);
11750
+ set(oauthNewClientIdAtom, value.clientId);
11751
+ }
11752
+ });
11753
+ var clearNewClientSecretAtom = atom(null, (get, set) => {
11754
+ set(oauthNewClientSecretAtom, null);
11755
+ set(oauthNewClientIdAtom, null);
11756
+ });
11757
+ var setOAuthClientsAtom = atom(null, (get, set, clients) => {
11758
+ set(oauthClientsAtom, clients);
11759
+ });
11760
+ var addOAuthClientAtom = atom(null, (get, set, client) => {
11761
+ const clients = get(oauthClientsAtom);
11762
+ set(oauthClientsAtom, [...clients, client]);
11763
+ });
11764
+ var updateOAuthClientAtom = atom(null, (get, set, updatedClient) => {
11765
+ const clients = get(oauthClientsAtom);
11766
+ const index = clients.findIndex((c) => c.id === updatedClient.id || c.clientId === updatedClient.clientId);
11767
+ if (index !== -1) {
11768
+ const newClients = [...clients];
11769
+ newClients[index] = updatedClient;
11770
+ set(oauthClientsAtom, newClients);
11771
+ }
11772
+ });
11773
+ var removeOAuthClientAtom = atom(null, (get, set, clientId) => {
11774
+ const clients = get(oauthClientsAtom);
11775
+ set(
11776
+ oauthClientsAtom,
11777
+ clients.filter((c) => c.id !== clientId && c.clientId !== clientId)
11778
+ );
11779
+ });
11780
+
11781
+ // src/features/oauth/hooks/useOAuthClients.ts
11782
+ function useOAuthClients() {
11783
+ const [clients, setClients] = useAtom3(oauthClientsAtom);
11784
+ const [isLoading, setIsLoading] = useAtom3(oauthClientsLoadingAtom);
11785
+ const [error, setError] = useAtom3(oauthClientsErrorAtom);
11786
+ const addClient = useSetAtom(addOAuthClientAtom);
11787
+ const setNewClientSecret = useSetAtom(setNewClientSecretAtom);
11788
+ const fetchClients = useCallback16(async () => {
11789
+ setIsLoading(true);
11790
+ setError(null);
11791
+ try {
11792
+ const fetchedClients = await OAuthService.listClients();
11793
+ setClients(fetchedClients);
11794
+ } catch (err) {
11795
+ console.error("[useOAuthClients] Failed to fetch clients:", err);
11796
+ setError(err instanceof Error ? err : new Error("Failed to fetch OAuth clients"));
11797
+ } finally {
11798
+ setIsLoading(false);
11799
+ }
11800
+ }, [setClients, setIsLoading, setError]);
11801
+ useEffect34(() => {
11802
+ fetchClients();
11803
+ }, [fetchClients]);
11804
+ const createClient = useCallback16(
11805
+ async (data) => {
11806
+ setIsLoading(true);
11807
+ setError(null);
11808
+ try {
11809
+ const result = await OAuthService.createClient(data);
11810
+ addClient(result.client);
11811
+ if (result.clientSecret) {
11812
+ setNewClientSecret({
11813
+ clientId: result.client.clientId,
11814
+ secret: result.clientSecret
11815
+ });
11816
+ }
11817
+ return result;
11818
+ } catch (err) {
11819
+ console.error("[useOAuthClients] Failed to create client:", err);
11820
+ const error2 = err instanceof Error ? err : new Error("Failed to create OAuth client");
11821
+ setError(error2);
11822
+ throw error2;
11823
+ } finally {
11824
+ setIsLoading(false);
11825
+ }
11826
+ },
11827
+ [addClient, setNewClientSecret, setIsLoading, setError]
11828
+ );
11829
+ return {
11830
+ clients,
11831
+ isLoading,
11832
+ error,
11833
+ refetch: fetchClients,
11834
+ createClient
11835
+ };
11836
+ }
11837
+ __name(useOAuthClients, "useOAuthClients");
11838
+
11839
+ // src/features/oauth/hooks/useOAuthClient.ts
11840
+ import { useAtomValue as useAtomValue2, useSetAtom as useSetAtom2 } from "jotai";
11841
+ import { useCallback as useCallback17, useEffect as useEffect35, useState as useState41 } from "react";
11842
+ function useOAuthClient(clientId) {
11843
+ const storedClient = useAtomValue2(oauthClientByIdAtom(clientId));
11844
+ const updateClientInStore = useSetAtom2(updateOAuthClientAtom);
11845
+ const removeClientFromStore = useSetAtom2(removeOAuthClientAtom);
11846
+ const setNewClientSecret = useSetAtom2(setNewClientSecretAtom);
11847
+ const [fetchedClient, setFetchedClient] = useState41(null);
11848
+ const [isLoading, setIsLoading] = useState41(false);
11849
+ const [error, setError] = useState41(null);
11850
+ const client = storedClient || fetchedClient;
11851
+ const fetchClient = useCallback17(async () => {
11852
+ if (!clientId) return;
11853
+ setIsLoading(true);
11854
+ setError(null);
11855
+ try {
11856
+ const fetched = await OAuthService.getClient({ clientId });
11857
+ setFetchedClient(fetched);
11858
+ } catch (err) {
11859
+ console.error("[useOAuthClient] Failed to fetch client:", err);
11860
+ setError(err instanceof Error ? err : new Error("Failed to fetch OAuth client"));
11861
+ } finally {
11862
+ setIsLoading(false);
11863
+ }
11864
+ }, [clientId]);
11865
+ useEffect35(() => {
11866
+ if (!storedClient && clientId) {
11867
+ fetchClient();
11868
+ }
11869
+ }, [storedClient, clientId, fetchClient]);
11870
+ const update = useCallback17(
11871
+ async (data) => {
11872
+ if (!clientId) throw new Error("No client ID");
11873
+ setIsLoading(true);
11874
+ setError(null);
11875
+ try {
11876
+ const updated = await OAuthService.updateClient({ clientId, data });
11877
+ updateClientInStore(updated);
11878
+ setFetchedClient(updated);
11879
+ } catch (err) {
11880
+ console.error("[useOAuthClient] Failed to update client:", err);
11881
+ const error2 = err instanceof Error ? err : new Error("Failed to update OAuth client");
11882
+ setError(error2);
11883
+ throw error2;
11884
+ } finally {
11885
+ setIsLoading(false);
11886
+ }
11887
+ },
11888
+ [clientId, updateClientInStore]
11889
+ );
11890
+ const deleteClient = useCallback17(async () => {
11891
+ if (!clientId) throw new Error("No client ID");
11892
+ setIsLoading(true);
11893
+ setError(null);
11894
+ try {
11895
+ await OAuthService.deleteClient({ clientId });
11896
+ removeClientFromStore(clientId);
11897
+ } catch (err) {
11898
+ console.error("[useOAuthClient] Failed to delete client:", err);
11899
+ const error2 = err instanceof Error ? err : new Error("Failed to delete OAuth client");
11900
+ setError(error2);
11901
+ throw error2;
11902
+ } finally {
11903
+ setIsLoading(false);
11904
+ }
11905
+ }, [clientId, removeClientFromStore]);
11906
+ const regenerateSecret = useCallback17(async () => {
11907
+ if (!clientId) throw new Error("No client ID");
11908
+ setIsLoading(true);
11909
+ setError(null);
11910
+ try {
11911
+ const result = await OAuthService.regenerateSecret({ clientId });
11912
+ setNewClientSecret({
11913
+ clientId,
11914
+ secret: result.clientSecret
11915
+ });
11916
+ return result.clientSecret;
11917
+ } catch (err) {
11918
+ console.error("[useOAuthClient] Failed to regenerate secret:", err);
11919
+ const error2 = err instanceof Error ? err : new Error("Failed to regenerate client secret");
11920
+ setError(error2);
11921
+ throw error2;
11922
+ } finally {
11923
+ setIsLoading(false);
11924
+ }
11925
+ }, [clientId, setNewClientSecret]);
11926
+ return {
11927
+ client,
11928
+ isLoading,
11929
+ error,
11930
+ update,
11931
+ deleteClient,
11932
+ regenerateSecret,
11933
+ refetch: fetchClient
11934
+ };
11935
+ }
11936
+ __name(useOAuthClient, "useOAuthClient");
11937
+
11938
+ // src/features/oauth/hooks/useOAuthConsent.ts
11939
+ import { useCallback as useCallback18, useEffect as useEffect36, useState as useState42 } from "react";
11940
+ function useOAuthConsent(params) {
11941
+ const [clientInfo, setClientInfo] = useState42(null);
11942
+ const [isLoading, setIsLoading] = useState42(true);
11943
+ const [error, setError] = useState42(null);
11944
+ const [isSubmitting, setIsSubmitting] = useState42(false);
11945
+ useEffect36(() => {
11946
+ const fetchInfo = /* @__PURE__ */ __name(async () => {
11947
+ if (!params.clientId || !params.redirectUri || !params.scope) {
11948
+ setError(new Error("Missing required authorization parameters"));
11949
+ setIsLoading(false);
11950
+ return;
11951
+ }
11952
+ setIsLoading(true);
11953
+ setError(null);
11954
+ try {
11955
+ const info = await OAuthService.getAuthorizationInfo(params);
11956
+ setClientInfo(info);
11957
+ } catch (err) {
11958
+ console.error("[useOAuthConsent] Failed to fetch authorization info:", err);
11959
+ setError(err instanceof Error ? err : new Error("Failed to load authorization info"));
11960
+ } finally {
11961
+ setIsLoading(false);
11962
+ }
11963
+ }, "fetchInfo");
11964
+ fetchInfo();
11965
+ }, [
11966
+ params.clientId,
11967
+ params.redirectUri,
11968
+ params.scope,
11969
+ params.state,
11970
+ params.codeChallenge,
11971
+ params.codeChallengeMethod
11972
+ ]);
11973
+ const approve = useCallback18(async () => {
11974
+ setIsSubmitting(true);
11975
+ setError(null);
11976
+ try {
11977
+ const result = await OAuthService.approveAuthorization(params);
11978
+ if (result.redirectUrl) {
11979
+ window.location.href = result.redirectUrl;
11980
+ }
11981
+ } catch (err) {
11982
+ console.error("[useOAuthConsent] Failed to approve authorization:", err);
11983
+ setError(err instanceof Error ? err : new Error("Failed to approve authorization"));
11984
+ setIsSubmitting(false);
11985
+ }
11986
+ }, [params]);
11987
+ const deny = useCallback18(async () => {
11988
+ setIsSubmitting(true);
11989
+ setError(null);
11990
+ try {
11991
+ const result = await OAuthService.denyAuthorization(params);
11992
+ if (result.redirectUrl) {
11993
+ window.location.href = result.redirectUrl;
11994
+ }
11995
+ } catch (err) {
11996
+ console.error("[useOAuthConsent] Failed to deny authorization:", err);
11997
+ setError(err instanceof Error ? err : new Error("Failed to deny authorization"));
11998
+ setIsSubmitting(false);
11999
+ }
12000
+ }, [params]);
12001
+ return {
12002
+ clientInfo,
12003
+ isLoading,
12004
+ error,
12005
+ approve,
12006
+ deny,
12007
+ isSubmitting
12008
+ };
12009
+ }
12010
+ __name(useOAuthConsent, "useOAuthConsent");
12011
+
11720
12012
  // src/client/index.ts
11721
12013
  registerTableGenerator("roles", useRoleTableStructure);
11722
12014
  registerTableGenerator("users", useUserTableStructure);
@@ -11730,17 +12022,17 @@ import { memo, useMemo as useMemo18 } from "react";
11730
12022
  // src/components/tables/ContentTableSearch.tsx
11731
12023
  import { RefreshCw, Search, X } from "lucide-react";
11732
12024
  import { useTranslations as useTranslations45 } from "next-intl";
11733
- import { useCallback as useCallback16, useEffect as useEffect34, useRef as useRef16, useState as useState41 } from "react";
12025
+ import { useCallback as useCallback19, useEffect as useEffect37, useRef as useRef16, useState as useState43 } from "react";
11734
12026
  import { jsx as jsx133, jsxs as jsxs76 } from "react/jsx-runtime";
11735
12027
  function ContentTableSearch({ data }) {
11736
12028
  const t = useTranslations45();
11737
12029
  const searchTermRef = useRef16("");
11738
12030
  const inputRef = useRef16(null);
11739
- const [searchTerm, setSearchTerm] = useState41("");
11740
- const [isFocused, setIsFocused] = useState41(false);
11741
- const [isSearching, setIsSearching] = useState41(false);
12031
+ const [searchTerm, setSearchTerm] = useState43("");
12032
+ const [isFocused, setIsFocused] = useState43(false);
12033
+ const [isSearching, setIsSearching] = useState43(false);
11742
12034
  const isExpanded = isFocused || searchTerm.length > 0;
11743
- const search = useCallback16(
12035
+ const search = useCallback19(
11744
12036
  async (searchedTerm) => {
11745
12037
  try {
11746
12038
  if (searchedTerm === searchTermRef.current) return;
@@ -11753,7 +12045,7 @@ function ContentTableSearch({ data }) {
11753
12045
  [searchTermRef, data]
11754
12046
  );
11755
12047
  const updateSearchTerm = useDebounce(search, 500);
11756
- useEffect34(() => {
12048
+ useEffect37(() => {
11757
12049
  setIsSearching(true);
11758
12050
  updateSearchTerm(searchTerm);
11759
12051
  }, [updateSearchTerm, searchTerm]);
@@ -11905,13 +12197,13 @@ var ContentListTable = memo(/* @__PURE__ */ __name(function ContentListTable2(pr
11905
12197
  import Image9 from "next/image";
11906
12198
 
11907
12199
  // src/features/auth/contexts/AuthContext.tsx
11908
- import { createContext as createContext14, useContext as useContext15, useMemo as useMemo19, useState as useState43 } from "react";
12200
+ import { createContext as createContext14, useContext as useContext15, useMemo as useMemo19, useState as useState45 } from "react";
11909
12201
 
11910
12202
  // src/features/auth/components/forms/Register.tsx
11911
12203
  import { zodResolver as zodResolver5 } from "@hookform/resolvers/zod";
11912
12204
  import { useTranslations as useTranslations46 } from "next-intl";
11913
12205
  import Image8 from "next/image";
11914
- import { useState as useState42 } from "react";
12206
+ import { useState as useState44 } from "react";
11915
12207
  import { useForm as useForm5 } from "react-hook-form";
11916
12208
  import { v4 as v44 } from "uuid";
11917
12209
  import { z as z5 } from "zod";
@@ -11919,7 +12211,7 @@ import { Fragment as Fragment20, jsx as jsx135, jsxs as jsxs78 } from "react/jsx
11919
12211
  function Register() {
11920
12212
  const t = useTranslations46();
11921
12213
  const { setComponentType } = useAuthContext();
11922
- const [showConfirmation, setShowConfirmation] = useState42(false);
12214
+ const [showConfirmation, setShowConfirmation] = useState44(false);
11923
12215
  const formSchema = z5.object({
11924
12216
  company: z5.string().min(1, {
11925
12217
  message: t(`generic.errors.missing_company_name`)
@@ -12043,8 +12335,8 @@ var AuthContextProvider = /* @__PURE__ */ __name(({
12043
12335
  initialComponentType,
12044
12336
  initialParams
12045
12337
  }) => {
12046
- const [componentType, setComponentType] = useState43(initialComponentType);
12047
- const [params, setParams] = useState43(initialParams);
12338
+ const [componentType, setComponentType] = useState45(initialComponentType);
12339
+ const [params, setParams] = useState45(initialParams);
12048
12340
  const activeComponent = useMemo19(() => {
12049
12341
  if (componentType === void 0) return null;
12050
12342
  switch (componentType) {
@@ -12137,17 +12429,17 @@ __name(LandingComponent, "LandingComponent");
12137
12429
  import { zodResolver as zodResolver6 } from "@hookform/resolvers/zod";
12138
12430
  import { useTranslations as useTranslations48 } from "next-intl";
12139
12431
  import Image11 from "next/image";
12140
- import { useEffect as useEffect35, useState as useState44 } from "react";
12432
+ import { useEffect as useEffect38, useState as useState46 } from "react";
12141
12433
  import { useForm as useForm6 } from "react-hook-form";
12142
12434
  import { toast as toast7 } from "sonner";
12143
12435
  import { z as z6 } from "zod";
12144
12436
  import { Fragment as Fragment22, jsx as jsx139, jsxs as jsxs80 } from "react/jsx-runtime";
12145
12437
  function AcceptInvitation() {
12146
12438
  const { setComponentType, params, setParams } = useAuthContext();
12147
- const [showConfirmation, setShowConfirmation] = useState44(false);
12148
- const [error, setError] = useState44(void 0);
12439
+ const [showConfirmation, setShowConfirmation] = useState46(false);
12440
+ const [error, setError] = useState46(void 0);
12149
12441
  const t = useTranslations48();
12150
- useEffect35(() => {
12442
+ useEffect38(() => {
12151
12443
  async function validateCode(code) {
12152
12444
  try {
12153
12445
  const payload = {
@@ -12240,15 +12532,15 @@ __name(AcceptInvitation, "AcceptInvitation");
12240
12532
  // src/features/auth/components/forms/ActivateAccount.tsx
12241
12533
  import { useTranslations as useTranslations49 } from "next-intl";
12242
12534
  import Image12 from "next/image";
12243
- import { useEffect as useEffect36, useState as useState45 } from "react";
12535
+ import { useEffect as useEffect39, useState as useState47 } from "react";
12244
12536
  import { toast as toast8 } from "sonner";
12245
12537
  import { Fragment as Fragment23, jsx as jsx140, jsxs as jsxs81 } from "react/jsx-runtime";
12246
12538
  function ActivateAccount() {
12247
12539
  const { setComponentType, params, setParams } = useAuthContext();
12248
- const [showConfirmation, setShowConfirmation] = useState45(false);
12249
- const [error, setError] = useState45(void 0);
12540
+ const [showConfirmation, setShowConfirmation] = useState47(false);
12541
+ const [error, setError] = useState47(void 0);
12250
12542
  const t = useTranslations49();
12251
- useEffect36(() => {
12543
+ useEffect39(() => {
12252
12544
  async function ActivateAccount2(code) {
12253
12545
  try {
12254
12546
  const payload = {
@@ -12289,12 +12581,12 @@ function ActivateAccount() {
12289
12581
  __name(ActivateAccount, "ActivateAccount");
12290
12582
 
12291
12583
  // src/features/auth/components/forms/Cookies.tsx
12292
- import { useEffect as useEffect37, useState as useState46 } from "react";
12584
+ import { useEffect as useEffect40, useState as useState48 } from "react";
12293
12585
  function Cookies({ dehydratedAuth, page }) {
12294
12586
  const { setUser } = useCurrentUserContext();
12295
12587
  const router = useI18nRouter();
12296
- const [hasSaved, setHasSaved] = useState46(false);
12297
- useEffect37(() => {
12588
+ const [hasSaved, setHasSaved] = useState48(false);
12589
+ useEffect40(() => {
12298
12590
  if (hasSaved) return;
12299
12591
  async function saveTokenOnServer() {
12300
12592
  await AuthService.saveToken({ dehydratedAuth });
@@ -12317,14 +12609,14 @@ __name(Cookies, "Cookies");
12317
12609
  import { zodResolver as zodResolver7 } from "@hookform/resolvers/zod";
12318
12610
  import { useTranslations as useTranslations50 } from "next-intl";
12319
12611
  import Image13 from "next/image";
12320
- import { useState as useState47 } from "react";
12612
+ import { useState as useState49 } from "react";
12321
12613
  import { useForm as useForm7 } from "react-hook-form";
12322
12614
  import { z as z7 } from "zod";
12323
12615
  import { Fragment as Fragment24, jsx as jsx141, jsxs as jsxs82 } from "react/jsx-runtime";
12324
12616
  function ForgotPassword() {
12325
12617
  const t = useTranslations50();
12326
12618
  const { setComponentType } = useAuthContext();
12327
- const [showConfirmation, setShowConfirmation] = useState47(false);
12619
+ const [showConfirmation, setShowConfirmation] = useState49(false);
12328
12620
  const formSchema = z7.object({
12329
12621
  email: z7.string().email({
12330
12622
  message: t(`generic.errors.invalid_email`)
@@ -12488,11 +12780,11 @@ function Login() {
12488
12780
  __name(Login, "Login");
12489
12781
 
12490
12782
  // src/features/auth/components/forms/Logout.tsx
12491
- import { useEffect as useEffect38 } from "react";
12783
+ import { useEffect as useEffect41 } from "react";
12492
12784
  import { Fragment as Fragment26, jsx as jsx143 } from "react/jsx-runtime";
12493
12785
  function Logout() {
12494
12786
  const generateUrl = usePageUrlGenerator();
12495
- useEffect38(() => {
12787
+ useEffect41(() => {
12496
12788
  const logOut = /* @__PURE__ */ __name(async () => {
12497
12789
  await AuthService.logout();
12498
12790
  window.location.href = generateUrl({ page: `/` });
@@ -12505,7 +12797,7 @@ __name(Logout, "Logout");
12505
12797
 
12506
12798
  // src/features/auth/components/forms/RefreshUser.tsx
12507
12799
  import { deleteCookie as deleteCookie2, getCookie as getCookie2 } from "cookies-next";
12508
- import { useEffect as useEffect39 } from "react";
12800
+ import { useEffect as useEffect42 } from "react";
12509
12801
  function RefreshUser() {
12510
12802
  const { setUser } = useCurrentUserContext();
12511
12803
  const loadFullUser = /* @__PURE__ */ __name(async () => {
@@ -12526,7 +12818,7 @@ function RefreshUser() {
12526
12818
  deleteCookie2("reloadData");
12527
12819
  }
12528
12820
  }, "loadFullUser");
12529
- useEffect39(() => {
12821
+ useEffect42(() => {
12530
12822
  const reloadData = getCookie2("reloadData");
12531
12823
  if (reloadData !== void 0) loadFullUser();
12532
12824
  }, []);
@@ -12538,17 +12830,17 @@ __name(RefreshUser, "RefreshUser");
12538
12830
  import { zodResolver as zodResolver9 } from "@hookform/resolvers/zod";
12539
12831
  import { useTranslations as useTranslations52 } from "next-intl";
12540
12832
  import Image15 from "next/image";
12541
- import { useEffect as useEffect40, useState as useState48 } from "react";
12833
+ import { useEffect as useEffect43, useState as useState50 } from "react";
12542
12834
  import { useForm as useForm9 } from "react-hook-form";
12543
12835
  import { toast as toast9 } from "sonner";
12544
12836
  import { z as z9 } from "zod";
12545
12837
  import { Fragment as Fragment27, jsx as jsx144, jsxs as jsxs84 } from "react/jsx-runtime";
12546
12838
  function ResetPassword() {
12547
12839
  const { setComponentType, params, setParams } = useAuthContext();
12548
- const [showConfirmation, setShowConfirmation] = useState48(false);
12549
- const [error, setError] = useState48(void 0);
12840
+ const [showConfirmation, setShowConfirmation] = useState50(false);
12841
+ const [error, setError] = useState50(void 0);
12550
12842
  const t = useTranslations52();
12551
- useEffect40(() => {
12843
+ useEffect43(() => {
12552
12844
  async function validateResetPasswordCode(code) {
12553
12845
  try {
12554
12846
  const payload = {
@@ -12830,7 +13122,7 @@ __name(NotificationsListContainer, "NotificationsListContainer");
12830
13122
  // src/features/notification/components/modals/NotificationModal.tsx
12831
13123
  import { BellIcon } from "lucide-react";
12832
13124
  import { useTranslations as useTranslations58 } from "next-intl";
12833
- import { Fragment as Fragment29, useCallback as useCallback17, useEffect as useEffect41, useMemo as useMemo20, useRef as useRef17, useState as useState49 } from "react";
13125
+ import { Fragment as Fragment29, useCallback as useCallback20, useEffect as useEffect44, useMemo as useMemo20, useRef as useRef17, useState as useState51 } from "react";
12834
13126
  import { toast as toast10 } from "sonner";
12835
13127
  import { jsx as jsx151, jsxs as jsxs89 } from "react/jsx-runtime";
12836
13128
  function NotificationModalContent({ isOpen, setIsOpen }) {
@@ -12850,14 +13142,14 @@ function NotificationModalContent({ isOpen, setIsOpen }) {
12850
13142
  const { socketNotifications, removeSocketNotification, clearSocketNotifications } = useSocketContext();
12851
13143
  const t = useTranslations58();
12852
13144
  const generateUrl = usePageUrlGenerator();
12853
- const [newNotifications, setNewNotifications] = useState49(false);
13145
+ const [newNotifications, setNewNotifications] = useState51(false);
12854
13146
  const preventAutoClose = useRef17(false);
12855
13147
  const circuitBreakerRef = useRef17({
12856
13148
  count: 0,
12857
13149
  resetTime: 0,
12858
13150
  isOpen: false
12859
13151
  });
12860
- const checkCircuitBreaker = useCallback17(() => {
13152
+ const checkCircuitBreaker = useCallback20(() => {
12861
13153
  const now = Date.now();
12862
13154
  const breaker = circuitBreakerRef.current;
12863
13155
  if (now > breaker.resetTime) {
@@ -12879,14 +13171,14 @@ function NotificationModalContent({ isOpen, setIsOpen }) {
12879
13171
  unreadIds: unreadNotifications2.map((notif) => notif.id)
12880
13172
  };
12881
13173
  }, [notifications]);
12882
- useEffect41(() => {
13174
+ useEffect44(() => {
12883
13175
  setNewNotifications(unreadCount > 0);
12884
13176
  }, [unreadCount]);
12885
- useEffect41(() => {
13177
+ useEffect44(() => {
12886
13178
  if (lastLoaded === 0) loadNotifications();
12887
13179
  }, [lastLoaded, loadNotifications]);
12888
13180
  const processSocketNotificationsRef = useRef17(null);
12889
- const processSocketNotifications = useCallback17(() => {
13181
+ const processSocketNotifications = useCallback20(() => {
12890
13182
  if (socketNotifications.length === 0) {
12891
13183
  return;
12892
13184
  }
@@ -12925,7 +13217,7 @@ function NotificationModalContent({ isOpen, setIsOpen }) {
12925
13217
  generateUrl,
12926
13218
  checkCircuitBreaker
12927
13219
  ]);
12928
- useEffect41(() => {
13220
+ useEffect44(() => {
12929
13221
  if (processSocketNotificationsRef.current) {
12930
13222
  clearTimeout(processSocketNotificationsRef.current);
12931
13223
  }
@@ -13059,13 +13351,13 @@ __name(FormRoles, "FormRoles");
13059
13351
 
13060
13352
  // src/features/role/components/forms/RemoveUserFromRole.tsx
13061
13353
  import { useTranslations as useTranslations61 } from "next-intl";
13062
- import { useEffect as useEffect42, useState as useState50 } from "react";
13354
+ import { useEffect as useEffect45, useState as useState52 } from "react";
13063
13355
  import { Fragment as Fragment32, jsx as jsx156, jsxs as jsxs92 } from "react/jsx-runtime";
13064
13356
  function RemoveUserFromRole({ role, user, refresh }) {
13065
- const [open, setOpen] = useState50(false);
13066
- const [canRemove, setCanRemove] = useState50(false);
13357
+ const [open, setOpen] = useState52(false);
13358
+ const [canRemove, setCanRemove] = useState52(false);
13067
13359
  const t = useTranslations61();
13068
- useEffect42(() => {
13360
+ useEffect45(() => {
13069
13361
  async function checkCompanyAdminDeletability() {
13070
13362
  const roleUsers = await UserService.findAllUsersByRole({
13071
13363
  roleId: role.id
@@ -13135,14 +13427,14 @@ __name(RemoveUserFromRole, "RemoveUserFromRole");
13135
13427
  // src/features/role/components/forms/UserRoleAdd.tsx
13136
13428
  import { PlusCircle } from "lucide-react";
13137
13429
  import { useTranslations as useTranslations62 } from "next-intl";
13138
- import { useCallback as useCallback18, useEffect as useEffect43, useRef as useRef18, useState as useState51 } from "react";
13430
+ import { useCallback as useCallback21, useEffect as useEffect46, useRef as useRef18, useState as useState53 } from "react";
13139
13431
  import { toast as toast11 } from "sonner";
13140
13432
  import { Fragment as Fragment33, jsx as jsx157, jsxs as jsxs93 } from "react/jsx-runtime";
13141
13433
  function UserRoleAdd({ user, refresh }) {
13142
- const [open, setOpen] = useState51(false);
13434
+ const [open, setOpen] = useState53(false);
13143
13435
  const inputRef = useRef18(null);
13144
- const [searchTerm, setSearchTerm] = useState51("");
13145
- const [roles, setRoles] = useState51([]);
13436
+ const [searchTerm, setSearchTerm] = useState53("");
13437
+ const [roles, setRoles] = useState53([]);
13146
13438
  const t = useTranslations62();
13147
13439
  const addUserToRole = /* @__PURE__ */ __name(async (role) => {
13148
13440
  await RoleService.addUserToRole({
@@ -13166,7 +13458,7 @@ function UserRoleAdd({ user, refresh }) {
13166
13458
  );
13167
13459
  refresh();
13168
13460
  }, "addUserToRole");
13169
- const searchRoles = useCallback18(
13461
+ const searchRoles = useCallback21(
13170
13462
  async (term) => {
13171
13463
  setRoles(
13172
13464
  await RoleService.findAllRolesUserNotIn({
@@ -13178,10 +13470,10 @@ function UserRoleAdd({ user, refresh }) {
13178
13470
  [searchTerm, user]
13179
13471
  );
13180
13472
  const updateSearchTerm = useDebounce(searchRoles, 500);
13181
- useEffect43(() => {
13473
+ useEffect46(() => {
13182
13474
  if (open) updateSearchTerm(searchTerm);
13183
13475
  }, [open, searchTerm]);
13184
- useEffect43(() => {
13476
+ useEffect46(() => {
13185
13477
  if (open) searchRoles("");
13186
13478
  }, [open]);
13187
13479
  return /* @__PURE__ */ jsxs93(Fragment33, { children: [
@@ -13275,6 +13567,893 @@ function UserRolesList({ user }) {
13275
13567
  }
13276
13568
  __name(UserRolesList, "UserRolesList");
13277
13569
 
13570
+ // src/features/oauth/components/OAuthRedirectUriInput.tsx
13571
+ import { useCallback as useCallback22 } from "react";
13572
+ import { Plus, Trash2 } from "lucide-react";
13573
+ import { jsx as jsx160, jsxs as jsxs94 } from "react/jsx-runtime";
13574
+ function isValidRedirectUri(uri) {
13575
+ if (!uri.trim()) return false;
13576
+ if (uri.startsWith("http://localhost") || uri.startsWith("http://127.0.0.1")) {
13577
+ return true;
13578
+ }
13579
+ if (uri.startsWith("https://")) {
13580
+ try {
13581
+ new URL(uri);
13582
+ return true;
13583
+ } catch {
13584
+ return false;
13585
+ }
13586
+ }
13587
+ if (uri.includes("://")) {
13588
+ const schemeMatch = uri.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):\/\//);
13589
+ return schemeMatch !== null;
13590
+ }
13591
+ return false;
13592
+ }
13593
+ __name(isValidRedirectUri, "isValidRedirectUri");
13594
+ function OAuthRedirectUriInput({
13595
+ value,
13596
+ onChange,
13597
+ error,
13598
+ disabled = false,
13599
+ label = "Redirect URIs"
13600
+ }) {
13601
+ const handleAdd = useCallback22(() => {
13602
+ onChange([...value, ""]);
13603
+ }, [value, onChange]);
13604
+ const handleRemove = useCallback22(
13605
+ (index) => {
13606
+ const newUris = value.filter((_, i) => i !== index);
13607
+ onChange(newUris.length > 0 ? newUris : [""]);
13608
+ },
13609
+ [value, onChange]
13610
+ );
13611
+ const handleChange = useCallback22(
13612
+ (index, newValue) => {
13613
+ const newUris = [...value];
13614
+ newUris[index] = newValue;
13615
+ onChange(newUris);
13616
+ },
13617
+ [value, onChange]
13618
+ );
13619
+ return /* @__PURE__ */ jsxs94("div", { className: "space-y-2", children: [
13620
+ /* @__PURE__ */ jsxs94(Label, { children: [
13621
+ label,
13622
+ " *"
13623
+ ] }),
13624
+ /* @__PURE__ */ jsx160("p", { className: "text-sm text-muted-foreground", children: "Enter the URIs where users will be redirected after authorization. Use https:// for production, or custom schemes for mobile apps." }),
13625
+ /* @__PURE__ */ jsx160("div", { className: "space-y-2", children: value.map((uri, index) => {
13626
+ const isValid3 = !uri || isValidRedirectUri(uri);
13627
+ return /* @__PURE__ */ jsxs94("div", { className: "flex gap-2", children: [
13628
+ /* @__PURE__ */ jsxs94("div", { className: "flex-1", children: [
13629
+ /* @__PURE__ */ jsx160(
13630
+ Input,
13631
+ {
13632
+ value: uri,
13633
+ onChange: (e) => handleChange(index, e.target.value),
13634
+ placeholder: "https://example.com/callback or myapp://oauth",
13635
+ disabled,
13636
+ className: !isValid3 ? "border-destructive" : ""
13637
+ }
13638
+ ),
13639
+ !isValid3 && /* @__PURE__ */ jsx160("p", { className: "text-xs text-destructive mt-1", children: "Must be https://, http://localhost, or a custom scheme (app://)" })
13640
+ ] }),
13641
+ /* @__PURE__ */ jsx160(
13642
+ Button,
13643
+ {
13644
+ type: "button",
13645
+ variant: "ghost",
13646
+ size: "icon",
13647
+ onClick: () => handleRemove(index),
13648
+ disabled: disabled || value.length === 1,
13649
+ title: "Remove URI",
13650
+ children: /* @__PURE__ */ jsx160(Trash2, { className: "h-4 w-4" })
13651
+ }
13652
+ )
13653
+ ] }, index);
13654
+ }) }),
13655
+ /* @__PURE__ */ jsxs94(
13656
+ Button,
13657
+ {
13658
+ type: "button",
13659
+ variant: "outline",
13660
+ size: "sm",
13661
+ onClick: handleAdd,
13662
+ disabled,
13663
+ className: "mt-2",
13664
+ children: [
13665
+ /* @__PURE__ */ jsx160(Plus, { className: "h-4 w-4 mr-2" }),
13666
+ "Add Redirect URI"
13667
+ ]
13668
+ }
13669
+ ),
13670
+ error && /* @__PURE__ */ jsx160("p", { className: "text-sm text-destructive", children: error })
13671
+ ] });
13672
+ }
13673
+ __name(OAuthRedirectUriInput, "OAuthRedirectUriInput");
13674
+
13675
+ // src/features/oauth/components/OAuthScopeSelector.tsx
13676
+ import { useCallback as useCallback23 } from "react";
13677
+ import { jsx as jsx161, jsxs as jsxs95 } from "react/jsx-runtime";
13678
+ function OAuthScopeSelector({
13679
+ value,
13680
+ onChange,
13681
+ availableScopes = AVAILABLE_OAUTH_SCOPES,
13682
+ disabled = false,
13683
+ error,
13684
+ label = "Allowed Scopes"
13685
+ }) {
13686
+ const handleToggle = useCallback23(
13687
+ (scope, checked) => {
13688
+ if (checked) {
13689
+ onChange([...value, scope]);
13690
+ } else {
13691
+ onChange(value.filter((s) => s !== scope));
13692
+ }
13693
+ },
13694
+ [value, onChange]
13695
+ );
13696
+ const groupedScopes = availableScopes.reduce((acc, scope) => {
13697
+ const [category] = scope.scope.split(":");
13698
+ const groupName = category === scope.scope ? "General" : category;
13699
+ if (!acc[groupName]) {
13700
+ acc[groupName] = [];
13701
+ }
13702
+ acc[groupName].push(scope);
13703
+ return acc;
13704
+ }, {});
13705
+ return /* @__PURE__ */ jsxs95("div", { className: "space-y-4", children: [
13706
+ /* @__PURE__ */ jsxs95("div", { children: [
13707
+ /* @__PURE__ */ jsxs95(Label, { children: [
13708
+ label,
13709
+ " *"
13710
+ ] }),
13711
+ /* @__PURE__ */ jsx161("p", { className: "text-sm text-muted-foreground", children: "Select the permissions your application needs." })
13712
+ ] }),
13713
+ /* @__PURE__ */ jsx161("div", { className: "space-y-4", children: Object.entries(groupedScopes).map(([groupName, scopes]) => /* @__PURE__ */ jsxs95("div", { className: "space-y-2", children: [
13714
+ /* @__PURE__ */ jsx161("h4", { className: "text-sm font-medium capitalize", children: groupName }),
13715
+ /* @__PURE__ */ jsx161("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-2 pl-2", children: scopes.map((scopeInfo) => {
13716
+ const isChecked = value.includes(scopeInfo.scope);
13717
+ const isAdmin = scopeInfo.scope === "admin";
13718
+ return /* @__PURE__ */ jsxs95(
13719
+ "div",
13720
+ {
13721
+ className: `flex items-start space-x-3 p-2 rounded-md border ${isChecked ? "bg-primary/5 border-primary/20" : "border-transparent"} ${isAdmin ? "bg-destructive/5" : ""}`,
13722
+ children: [
13723
+ /* @__PURE__ */ jsx161(
13724
+ Checkbox,
13725
+ {
13726
+ id: `scope-${scopeInfo.scope}`,
13727
+ checked: isChecked,
13728
+ onCheckedChange: (checked) => handleToggle(scopeInfo.scope, checked === true),
13729
+ disabled
13730
+ }
13731
+ ),
13732
+ /* @__PURE__ */ jsxs95("div", { className: "flex-1", children: [
13733
+ /* @__PURE__ */ jsxs95(
13734
+ Label,
13735
+ {
13736
+ htmlFor: `scope-${scopeInfo.scope}`,
13737
+ className: "text-sm font-medium cursor-pointer",
13738
+ children: [
13739
+ scopeInfo.name,
13740
+ isAdmin && /* @__PURE__ */ jsx161("span", { className: "ml-2 text-xs text-destructive", children: "(Dangerous)" })
13741
+ ]
13742
+ }
13743
+ ),
13744
+ /* @__PURE__ */ jsx161("p", { className: "text-xs text-muted-foreground", children: scopeInfo.description })
13745
+ ] })
13746
+ ]
13747
+ },
13748
+ scopeInfo.scope
13749
+ );
13750
+ }) })
13751
+ ] }, groupName)) }),
13752
+ error && /* @__PURE__ */ jsx161("p", { className: "text-sm text-destructive", children: error })
13753
+ ] });
13754
+ }
13755
+ __name(OAuthScopeSelector, "OAuthScopeSelector");
13756
+
13757
+ // src/features/oauth/components/OAuthClientSecretDisplay.tsx
13758
+ import { useState as useState54, useCallback as useCallback24 } from "react";
13759
+ import { Copy, Check, AlertTriangle } from "lucide-react";
13760
+ import { jsx as jsx162, jsxs as jsxs96 } from "react/jsx-runtime";
13761
+ function OAuthClientSecretDisplay({
13762
+ secret,
13763
+ onDismiss,
13764
+ open,
13765
+ clientName
13766
+ }) {
13767
+ const [copied, setCopied] = useState54(false);
13768
+ const handleCopy = useCallback24(async () => {
13769
+ try {
13770
+ await navigator.clipboard.writeText(secret);
13771
+ setCopied(true);
13772
+ setTimeout(() => setCopied(false), 2e3);
13773
+ } catch (err) {
13774
+ console.error("Failed to copy to clipboard:", err);
13775
+ }
13776
+ }, [secret]);
13777
+ const handleDismiss = useCallback24(() => {
13778
+ setCopied(false);
13779
+ onDismiss();
13780
+ }, [onDismiss]);
13781
+ return /* @__PURE__ */ jsx162(Dialog, { open, onOpenChange: (isOpen) => !isOpen && handleDismiss(), children: /* @__PURE__ */ jsxs96(DialogContent, { className: "sm:max-w-md", children: [
13782
+ /* @__PURE__ */ jsxs96(DialogHeader, { children: [
13783
+ /* @__PURE__ */ jsxs96(DialogTitle, { className: "flex items-center gap-2", children: [
13784
+ /* @__PURE__ */ jsx162(AlertTriangle, { className: "h-5 w-5 text-warning" }),
13785
+ "Save Your Client Secret"
13786
+ ] }),
13787
+ /* @__PURE__ */ jsx162(DialogDescription, { children: clientName ? `Your client secret for "${clientName}" is shown below.` : "Your client secret is shown below." })
13788
+ ] }),
13789
+ /* @__PURE__ */ jsxs96(Alert, { variant: "destructive", className: "my-4", children: [
13790
+ /* @__PURE__ */ jsx162(AlertTriangle, { className: "h-4 w-4" }),
13791
+ /* @__PURE__ */ jsxs96(AlertDescription, { children: [
13792
+ /* @__PURE__ */ jsx162("strong", { children: "This is the only time your client secret will be displayed." }),
13793
+ /* @__PURE__ */ jsx162("br", {}),
13794
+ "Copy it now and store it securely. You will not be able to retrieve it later."
13795
+ ] })
13796
+ ] }),
13797
+ /* @__PURE__ */ jsxs96("div", { className: "flex items-center space-x-2", children: [
13798
+ /* @__PURE__ */ jsx162("div", { className: "flex-1", children: /* @__PURE__ */ jsx162(
13799
+ Input,
13800
+ {
13801
+ value: secret,
13802
+ readOnly: true,
13803
+ className: "font-mono text-sm",
13804
+ onClick: (e) => e.currentTarget.select()
13805
+ }
13806
+ ) }),
13807
+ /* @__PURE__ */ jsx162(
13808
+ Button,
13809
+ {
13810
+ type: "button",
13811
+ variant: "outline",
13812
+ size: "icon",
13813
+ onClick: handleCopy,
13814
+ title: copied ? "Copied!" : "Copy to clipboard",
13815
+ children: copied ? /* @__PURE__ */ jsx162(Check, { className: "h-4 w-4 text-green-600" }) : /* @__PURE__ */ jsx162(Copy, { className: "h-4 w-4" })
13816
+ }
13817
+ )
13818
+ ] }),
13819
+ copied && /* @__PURE__ */ jsx162("p", { className: "text-sm text-green-600 text-center", children: "Copied to clipboard!" }),
13820
+ /* @__PURE__ */ jsx162(DialogFooter, { className: "mt-4", children: /* @__PURE__ */ jsx162(Button, { onClick: handleDismiss, className: "w-full", children: "I've Saved My Secret" }) })
13821
+ ] }) });
13822
+ }
13823
+ __name(OAuthClientSecretDisplay, "OAuthClientSecretDisplay");
13824
+
13825
+ // src/features/oauth/components/OAuthClientCard.tsx
13826
+ import { formatDistanceToNow } from "date-fns";
13827
+ import { Key, MoreVertical, Pencil, Trash2 as Trash22 } from "lucide-react";
13828
+ import { jsx as jsx163, jsxs as jsxs97 } from "react/jsx-runtime";
13829
+ function OAuthClientCard({
13830
+ client,
13831
+ onClick,
13832
+ onEdit,
13833
+ onDelete
13834
+ }) {
13835
+ const truncatedId = client.clientId.length > 12 ? `${client.clientId.slice(0, 8)}...${client.clientId.slice(-4)}` : client.clientId;
13836
+ const createdAgo = client.createdAt ? formatDistanceToNow(new Date(client.createdAt), { addSuffix: true }) : "Unknown";
13837
+ return /* @__PURE__ */ jsxs97(
13838
+ Card,
13839
+ {
13840
+ className: `cursor-pointer transition-colors hover:bg-accent/50 ${!client.isActive ? "opacity-60" : ""}`,
13841
+ onClick,
13842
+ children: [
13843
+ /* @__PURE__ */ jsxs97(CardHeader, { className: "pb-2", children: [
13844
+ /* @__PURE__ */ jsxs97("div", { className: "flex items-start justify-between", children: [
13845
+ /* @__PURE__ */ jsxs97("div", { className: "flex items-center gap-2", children: [
13846
+ /* @__PURE__ */ jsx163(Key, { className: "h-5 w-5 text-muted-foreground" }),
13847
+ /* @__PURE__ */ jsx163(CardTitle, { className: "text-lg", children: client.name })
13848
+ ] }),
13849
+ /* @__PURE__ */ jsxs97("div", { className: "flex items-center gap-2", children: [
13850
+ /* @__PURE__ */ jsx163(Badge, { variant: client.isActive ? "default" : "secondary", children: client.isActive ? "Active" : "Inactive" }),
13851
+ (onEdit || onDelete) && /* @__PURE__ */ jsxs97(DropdownMenu, { children: [
13852
+ /* @__PURE__ */ jsx163(DropdownMenuTrigger, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx163(Button, { render: /* @__PURE__ */ jsx163("div", {}), nativeButton: false, variant: "ghost", size: "icon", className: "h-8 w-8", children: /* @__PURE__ */ jsx163(MoreVertical, { className: "h-4 w-4" }) }) }),
13853
+ /* @__PURE__ */ jsxs97(DropdownMenuContent, { align: "end", children: [
13854
+ onEdit && /* @__PURE__ */ jsxs97(DropdownMenuItem, { onClick: (e) => {
13855
+ e.stopPropagation();
13856
+ onEdit();
13857
+ }, children: [
13858
+ /* @__PURE__ */ jsx163(Pencil, { className: "h-4 w-4 mr-2" }),
13859
+ "Edit"
13860
+ ] }),
13861
+ onDelete && /* @__PURE__ */ jsxs97(
13862
+ DropdownMenuItem,
13863
+ {
13864
+ onClick: (e) => {
13865
+ e.stopPropagation();
13866
+ onDelete();
13867
+ },
13868
+ className: "text-destructive",
13869
+ children: [
13870
+ /* @__PURE__ */ jsx163(Trash22, { className: "h-4 w-4 mr-2" }),
13871
+ "Delete"
13872
+ ]
13873
+ }
13874
+ )
13875
+ ] })
13876
+ ] })
13877
+ ] })
13878
+ ] }),
13879
+ client.description && /* @__PURE__ */ jsx163(CardDescription, { className: "line-clamp-2", children: client.description })
13880
+ ] }),
13881
+ /* @__PURE__ */ jsx163(CardContent, { children: /* @__PURE__ */ jsxs97("div", { className: "flex flex-wrap gap-x-4 gap-y-1 text-sm text-muted-foreground", children: [
13882
+ /* @__PURE__ */ jsx163("span", { className: "font-mono", children: truncatedId }),
13883
+ /* @__PURE__ */ jsxs97("span", { children: [
13884
+ "Created ",
13885
+ createdAgo
13886
+ ] }),
13887
+ /* @__PURE__ */ jsx163("span", { children: client.isConfidential ? "Confidential" : "Public" })
13888
+ ] }) })
13889
+ ]
13890
+ }
13891
+ );
13892
+ }
13893
+ __name(OAuthClientCard, "OAuthClientCard");
13894
+
13895
+ // src/features/oauth/components/OAuthClientList.tsx
13896
+ import { Plus as Plus2, Key as Key2 } from "lucide-react";
13897
+ import { jsx as jsx164, jsxs as jsxs98 } from "react/jsx-runtime";
13898
+ function OAuthClientList({
13899
+ clients,
13900
+ isLoading = false,
13901
+ error,
13902
+ onClientClick,
13903
+ onCreateClick,
13904
+ onEditClick,
13905
+ onDeleteClick,
13906
+ emptyStateMessage = "No OAuth applications yet. Create one to get started.",
13907
+ title = "OAuth Applications"
13908
+ }) {
13909
+ if (isLoading && clients.length === 0) {
13910
+ return /* @__PURE__ */ jsxs98("div", { className: "space-y-4", children: [
13911
+ /* @__PURE__ */ jsxs98("div", { className: "flex items-center justify-between", children: [
13912
+ /* @__PURE__ */ jsx164("h2", { className: "text-2xl font-bold", children: title }),
13913
+ /* @__PURE__ */ jsx164(Skeleton, { className: "h-10 w-32" })
13914
+ ] }),
13915
+ /* @__PURE__ */ jsx164("div", { className: "space-y-3", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsx164(Skeleton, { className: "h-32 w-full" }, i)) })
13916
+ ] });
13917
+ }
13918
+ if (error) {
13919
+ return /* @__PURE__ */ jsxs98("div", { className: "space-y-4", children: [
13920
+ /* @__PURE__ */ jsx164("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx164("h2", { className: "text-2xl font-bold", children: title }) }),
13921
+ /* @__PURE__ */ jsx164("div", { className: "rounded-lg border border-destructive/50 bg-destructive/10 p-6 text-center", children: /* @__PURE__ */ jsx164("p", { className: "text-destructive", children: error.message }) })
13922
+ ] });
13923
+ }
13924
+ if (clients.length === 0) {
13925
+ return /* @__PURE__ */ jsxs98("div", { className: "space-y-4", children: [
13926
+ /* @__PURE__ */ jsxs98("div", { className: "flex items-center justify-between", children: [
13927
+ /* @__PURE__ */ jsx164("h2", { className: "text-2xl font-bold", children: title }),
13928
+ onCreateClick && /* @__PURE__ */ jsxs98(Button, { onClick: onCreateClick, children: [
13929
+ /* @__PURE__ */ jsx164(Plus2, { className: "h-4 w-4 mr-2" }),
13930
+ "New App"
13931
+ ] })
13932
+ ] }),
13933
+ /* @__PURE__ */ jsxs98("div", { className: "rounded-lg border border-dashed p-12 text-center", children: [
13934
+ /* @__PURE__ */ jsx164(Key2, { className: "h-12 w-12 mx-auto text-muted-foreground mb-4" }),
13935
+ /* @__PURE__ */ jsx164("h3", { className: "text-lg font-medium mb-2", children: "No OAuth Applications" }),
13936
+ /* @__PURE__ */ jsx164("p", { className: "text-muted-foreground mb-4", children: emptyStateMessage }),
13937
+ onCreateClick && /* @__PURE__ */ jsxs98(Button, { onClick: onCreateClick, children: [
13938
+ /* @__PURE__ */ jsx164(Plus2, { className: "h-4 w-4 mr-2" }),
13939
+ "Create Application"
13940
+ ] })
13941
+ ] })
13942
+ ] });
13943
+ }
13944
+ return /* @__PURE__ */ jsxs98("div", { className: "space-y-4", children: [
13945
+ /* @__PURE__ */ jsxs98("div", { className: "flex items-center justify-between", children: [
13946
+ /* @__PURE__ */ jsx164("h2", { className: "text-2xl font-bold", children: title }),
13947
+ onCreateClick && /* @__PURE__ */ jsxs98(Button, { onClick: onCreateClick, children: [
13948
+ /* @__PURE__ */ jsx164(Plus2, { className: "h-4 w-4 mr-2" }),
13949
+ "New App"
13950
+ ] })
13951
+ ] }),
13952
+ /* @__PURE__ */ jsx164("div", { className: "space-y-3", children: clients.map((client) => /* @__PURE__ */ jsx164(
13953
+ OAuthClientCard,
13954
+ {
13955
+ client,
13956
+ onClick: () => onClientClick?.(client),
13957
+ onEdit: onEditClick ? () => onEditClick(client) : void 0,
13958
+ onDelete: onDeleteClick ? () => onDeleteClick(client) : void 0
13959
+ },
13960
+ client.id || client.clientId
13961
+ )) })
13962
+ ] });
13963
+ }
13964
+ __name(OAuthClientList, "OAuthClientList");
13965
+
13966
+ // src/features/oauth/components/OAuthClientForm.tsx
13967
+ import { useState as useState55, useCallback as useCallback25 } from "react";
13968
+ import { jsx as jsx165, jsxs as jsxs99 } from "react/jsx-runtime";
13969
+ function OAuthClientForm({
13970
+ client,
13971
+ onSubmit,
13972
+ onCancel,
13973
+ isLoading = false
13974
+ }) {
13975
+ const isEditMode = !!client;
13976
+ const [formState, setFormState] = useState55({
13977
+ name: client?.name || "",
13978
+ description: client?.description || "",
13979
+ redirectUris: client?.redirectUris?.length ? client.redirectUris : [""],
13980
+ allowedScopes: client?.allowedScopes || [],
13981
+ isConfidential: client?.isConfidential ?? true
13982
+ });
13983
+ const [errors, setErrors] = useState55({});
13984
+ const validate = useCallback25(() => {
13985
+ const newErrors = {};
13986
+ if (!formState.name.trim()) {
13987
+ newErrors.name = "Application name is required";
13988
+ }
13989
+ const validUris = formState.redirectUris.filter((uri) => uri.trim());
13990
+ if (validUris.length === 0) {
13991
+ newErrors.redirectUris = "At least one redirect URI is required";
13992
+ }
13993
+ if (formState.allowedScopes.length === 0) {
13994
+ newErrors.allowedScopes = "At least one scope must be selected";
13995
+ }
13996
+ setErrors(newErrors);
13997
+ return Object.keys(newErrors).length === 0;
13998
+ }, [formState]);
13999
+ const handleSubmit = useCallback25(
14000
+ async (e) => {
14001
+ e.preventDefault();
14002
+ if (!validate()) return;
14003
+ const data = {
14004
+ name: formState.name.trim(),
14005
+ description: formState.description.trim() || void 0,
14006
+ redirectUris: formState.redirectUris.filter((uri) => uri.trim()),
14007
+ allowedScopes: formState.allowedScopes,
14008
+ allowedGrantTypes: DEFAULT_GRANT_TYPES,
14009
+ isConfidential: formState.isConfidential
14010
+ };
14011
+ await onSubmit(data);
14012
+ },
14013
+ [formState, validate, onSubmit]
14014
+ );
14015
+ return /* @__PURE__ */ jsx165(Card, { children: /* @__PURE__ */ jsxs99("form", { onSubmit: handleSubmit, children: [
14016
+ /* @__PURE__ */ jsxs99(CardHeader, { children: [
14017
+ /* @__PURE__ */ jsx165(CardTitle, { children: isEditMode ? "Edit Application" : "Create OAuth Application" }),
14018
+ /* @__PURE__ */ jsx165(CardDescription, { children: isEditMode ? "Update your OAuth application settings." : "Register a new application to access the API." })
14019
+ ] }),
14020
+ /* @__PURE__ */ jsxs99(CardContent, { className: "space-y-6", children: [
14021
+ /* @__PURE__ */ jsxs99("div", { className: "space-y-2", children: [
14022
+ /* @__PURE__ */ jsx165(Label, { htmlFor: "name", children: "Application Name *" }),
14023
+ /* @__PURE__ */ jsx165(
14024
+ Input,
14025
+ {
14026
+ id: "name",
14027
+ value: formState.name,
14028
+ onChange: (e) => setFormState((s) => ({ ...s, name: e.target.value })),
14029
+ placeholder: "My Lightroom Plugin",
14030
+ disabled: isLoading,
14031
+ className: errors.name ? "border-destructive" : ""
14032
+ }
14033
+ ),
14034
+ errors.name && /* @__PURE__ */ jsx165("p", { className: "text-sm text-destructive", children: errors.name })
14035
+ ] }),
14036
+ /* @__PURE__ */ jsxs99("div", { className: "space-y-2", children: [
14037
+ /* @__PURE__ */ jsx165(Label, { htmlFor: "description", children: "Description" }),
14038
+ /* @__PURE__ */ jsx165(
14039
+ Textarea,
14040
+ {
14041
+ id: "description",
14042
+ value: formState.description,
14043
+ onChange: (e) => setFormState((s) => ({ ...s, description: e.target.value })),
14044
+ placeholder: "A brief description of your application",
14045
+ disabled: isLoading,
14046
+ rows: 3
14047
+ }
14048
+ )
14049
+ ] }),
14050
+ /* @__PURE__ */ jsx165(
14051
+ OAuthRedirectUriInput,
14052
+ {
14053
+ value: formState.redirectUris,
14054
+ onChange: (uris) => setFormState((s) => ({ ...s, redirectUris: uris })),
14055
+ error: errors.redirectUris,
14056
+ disabled: isLoading
14057
+ }
14058
+ ),
14059
+ /* @__PURE__ */ jsx165(
14060
+ OAuthScopeSelector,
14061
+ {
14062
+ value: formState.allowedScopes,
14063
+ onChange: (scopes) => setFormState((s) => ({ ...s, allowedScopes: scopes })),
14064
+ error: errors.allowedScopes,
14065
+ disabled: isLoading
14066
+ }
14067
+ ),
14068
+ /* @__PURE__ */ jsxs99("div", { className: "space-y-3", children: [
14069
+ /* @__PURE__ */ jsx165(Label, { children: "Client Type" }),
14070
+ /* @__PURE__ */ jsxs99(
14071
+ RadioGroup,
14072
+ {
14073
+ value: formState.isConfidential ? "confidential" : "public",
14074
+ onValueChange: (v) => setFormState((s) => ({ ...s, isConfidential: v === "confidential" })),
14075
+ disabled: isLoading || isEditMode,
14076
+ children: [
14077
+ /* @__PURE__ */ jsxs99("div", { className: "flex items-start space-x-3 p-3 rounded-md border", children: [
14078
+ /* @__PURE__ */ jsx165(RadioGroupItem, { value: "confidential", id: "confidential", className: "mt-1" }),
14079
+ /* @__PURE__ */ jsxs99("div", { children: [
14080
+ /* @__PURE__ */ jsx165(Label, { htmlFor: "confidential", className: "font-medium cursor-pointer", children: "Confidential" }),
14081
+ /* @__PURE__ */ jsx165("p", { className: "text-sm text-muted-foreground", children: "Server-side application that can securely store the client secret." })
14082
+ ] })
14083
+ ] }),
14084
+ /* @__PURE__ */ jsxs99("div", { className: "flex items-start space-x-3 p-3 rounded-md border", children: [
14085
+ /* @__PURE__ */ jsx165(RadioGroupItem, { value: "public", id: "public", className: "mt-1" }),
14086
+ /* @__PURE__ */ jsxs99("div", { children: [
14087
+ /* @__PURE__ */ jsx165(Label, { htmlFor: "public", className: "font-medium cursor-pointer", children: "Public" }),
14088
+ /* @__PURE__ */ jsx165("p", { className: "text-sm text-muted-foreground", children: "Mobile or desktop application. Requires PKCE for authorization." })
14089
+ ] })
14090
+ ] })
14091
+ ]
14092
+ }
14093
+ ),
14094
+ isEditMode && /* @__PURE__ */ jsx165("p", { className: "text-sm text-muted-foreground", children: "Client type cannot be changed after creation." })
14095
+ ] })
14096
+ ] }),
14097
+ /* @__PURE__ */ jsxs99(CardFooter, { className: "flex justify-end gap-3", children: [
14098
+ /* @__PURE__ */ jsx165(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: isLoading, children: "Cancel" }),
14099
+ /* @__PURE__ */ jsx165(Button, { type: "submit", disabled: isLoading, children: isLoading ? "Saving..." : isEditMode ? "Save Changes" : "Create Application" })
14100
+ ] })
14101
+ ] }) });
14102
+ }
14103
+ __name(OAuthClientForm, "OAuthClientForm");
14104
+
14105
+ // src/features/oauth/components/OAuthClientDetail.tsx
14106
+ import { useState as useState56, useCallback as useCallback26 } from "react";
14107
+ import { format as format2 } from "date-fns";
14108
+ import { Copy as Copy2, Check as Check2, RefreshCw as RefreshCw2, Pencil as Pencil2, Trash2 as Trash23, ExternalLink } from "lucide-react";
14109
+ import { Fragment as Fragment34, jsx as jsx166, jsxs as jsxs100 } from "react/jsx-runtime";
14110
+ function OAuthClientDetail({
14111
+ client,
14112
+ isLoading = false,
14113
+ onEdit,
14114
+ onDelete,
14115
+ onRegenerateSecret
14116
+ }) {
14117
+ const [copiedField, setCopiedField] = useState56(null);
14118
+ const [showDeleteConfirm, setShowDeleteConfirm] = useState56(false);
14119
+ const [showRegenerateConfirm, setShowRegenerateConfirm] = useState56(false);
14120
+ const [isDeleting, setIsDeleting] = useState56(false);
14121
+ const [isRegenerating, setIsRegenerating] = useState56(false);
14122
+ const copyToClipboard = useCallback26(async (text, field) => {
14123
+ try {
14124
+ await navigator.clipboard.writeText(text);
14125
+ setCopiedField(field);
14126
+ setTimeout(() => setCopiedField(null), 2e3);
14127
+ } catch (err) {
14128
+ console.error("Failed to copy:", err);
14129
+ }
14130
+ }, []);
14131
+ const handleDelete = useCallback26(async () => {
14132
+ if (!onDelete) return;
14133
+ setIsDeleting(true);
14134
+ try {
14135
+ await onDelete();
14136
+ } finally {
14137
+ setIsDeleting(false);
14138
+ setShowDeleteConfirm(false);
14139
+ }
14140
+ }, [onDelete]);
14141
+ const handleRegenerateSecret = useCallback26(async () => {
14142
+ if (!onRegenerateSecret) return;
14143
+ setIsRegenerating(true);
14144
+ try {
14145
+ await onRegenerateSecret();
14146
+ } finally {
14147
+ setIsRegenerating(false);
14148
+ setShowRegenerateConfirm(false);
14149
+ }
14150
+ }, [onRegenerateSecret]);
14151
+ const createdDate = client.createdAt ? format2(new Date(client.createdAt), "MMMM d, yyyy") : "Unknown";
14152
+ return /* @__PURE__ */ jsxs100(Fragment34, { children: [
14153
+ /* @__PURE__ */ jsxs100(Card, { children: [
14154
+ /* @__PURE__ */ jsx166(CardHeader, { children: /* @__PURE__ */ jsxs100("div", { className: "flex items-start justify-between", children: [
14155
+ /* @__PURE__ */ jsxs100("div", { children: [
14156
+ /* @__PURE__ */ jsx166(CardTitle, { className: "text-2xl", children: client.name }),
14157
+ client.description && /* @__PURE__ */ jsx166(CardDescription, { className: "mt-1", children: client.description })
14158
+ ] }),
14159
+ /* @__PURE__ */ jsxs100("div", { className: "flex items-center gap-2", children: [
14160
+ /* @__PURE__ */ jsx166(Badge, { variant: client.isActive ? "default" : "secondary", children: client.isActive ? "Active" : "Inactive" }),
14161
+ /* @__PURE__ */ jsx166(Badge, { variant: "outline", children: client.isConfidential ? "Confidential" : "Public" })
14162
+ ] })
14163
+ ] }) }),
14164
+ /* @__PURE__ */ jsxs100(CardContent, { className: "space-y-6", children: [
14165
+ /* @__PURE__ */ jsxs100("div", { className: "space-y-2", children: [
14166
+ /* @__PURE__ */ jsx166(Label, { children: "Client ID" }),
14167
+ /* @__PURE__ */ jsxs100("div", { className: "flex gap-2", children: [
14168
+ /* @__PURE__ */ jsx166(Input, { value: client.clientId, readOnly: true, className: "font-mono" }),
14169
+ /* @__PURE__ */ jsx166(
14170
+ Button,
14171
+ {
14172
+ variant: "outline",
14173
+ size: "icon",
14174
+ onClick: () => copyToClipboard(client.clientId, "clientId"),
14175
+ title: "Copy Client ID",
14176
+ children: copiedField === "clientId" ? /* @__PURE__ */ jsx166(Check2, { className: "h-4 w-4 text-green-600" }) : /* @__PURE__ */ jsx166(Copy2, { className: "h-4 w-4" })
14177
+ }
14178
+ )
14179
+ ] })
14180
+ ] }),
14181
+ /* @__PURE__ */ jsxs100("div", { className: "space-y-2", children: [
14182
+ /* @__PURE__ */ jsx166(Label, { children: "Client Secret" }),
14183
+ /* @__PURE__ */ jsxs100("div", { className: "flex gap-2", children: [
14184
+ /* @__PURE__ */ jsx166(Input, { value: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", readOnly: true, className: "font-mono" }),
14185
+ onRegenerateSecret && /* @__PURE__ */ jsx166(
14186
+ Button,
14187
+ {
14188
+ variant: "outline",
14189
+ size: "icon",
14190
+ onClick: () => setShowRegenerateConfirm(true),
14191
+ title: "Regenerate Secret",
14192
+ disabled: isLoading,
14193
+ children: /* @__PURE__ */ jsx166(RefreshCw2, { className: "h-4 w-4" })
14194
+ }
14195
+ )
14196
+ ] }),
14197
+ /* @__PURE__ */ jsx166("p", { className: "text-xs text-muted-foreground", children: "Regenerating will invalidate the current secret and all existing tokens." })
14198
+ ] }),
14199
+ /* @__PURE__ */ jsx166(Separator, {}),
14200
+ /* @__PURE__ */ jsxs100("div", { className: "space-y-2", children: [
14201
+ /* @__PURE__ */ jsx166(Label, { children: "Redirect URIs" }),
14202
+ /* @__PURE__ */ jsx166("ul", { className: "space-y-1", children: client.redirectUris.map((uri, index) => /* @__PURE__ */ jsxs100("li", { className: "flex items-center gap-2 text-sm font-mono", children: [
14203
+ /* @__PURE__ */ jsx166(ExternalLink, { className: "h-3 w-3 text-muted-foreground" }),
14204
+ uri
14205
+ ] }, index)) })
14206
+ ] }),
14207
+ /* @__PURE__ */ jsxs100("div", { className: "space-y-2", children: [
14208
+ /* @__PURE__ */ jsx166(Label, { children: "Allowed Scopes" }),
14209
+ /* @__PURE__ */ jsx166("div", { className: "flex flex-wrap gap-2", children: client.allowedScopes.map((scope) => /* @__PURE__ */ jsx166(Badge, { variant: "secondary", children: OAUTH_SCOPE_DISPLAY[scope]?.name || scope }, scope)) })
14210
+ ] }),
14211
+ /* @__PURE__ */ jsxs100("div", { className: "space-y-2", children: [
14212
+ /* @__PURE__ */ jsx166(Label, { children: "Grant Types" }),
14213
+ /* @__PURE__ */ jsx166("div", { className: "flex flex-wrap gap-2", children: client.allowedGrantTypes.map((grant) => /* @__PURE__ */ jsx166(Badge, { variant: "outline", children: grant.replace(/_/g, " ") }, grant)) })
14214
+ ] }),
14215
+ /* @__PURE__ */ jsx166(Separator, {}),
14216
+ /* @__PURE__ */ jsx166("div", { className: "flex flex-wrap gap-x-6 gap-y-2 text-sm text-muted-foreground", children: /* @__PURE__ */ jsxs100("span", { children: [
14217
+ "Created: ",
14218
+ createdDate
14219
+ ] }) }),
14220
+ /* @__PURE__ */ jsxs100("div", { className: "flex gap-3 pt-4", children: [
14221
+ onEdit && /* @__PURE__ */ jsxs100(Button, { variant: "outline", onClick: onEdit, disabled: isLoading, children: [
14222
+ /* @__PURE__ */ jsx166(Pencil2, { className: "h-4 w-4 mr-2" }),
14223
+ "Edit"
14224
+ ] }),
14225
+ onDelete && /* @__PURE__ */ jsxs100(
14226
+ Button,
14227
+ {
14228
+ variant: "destructive",
14229
+ onClick: () => setShowDeleteConfirm(true),
14230
+ disabled: isLoading,
14231
+ children: [
14232
+ /* @__PURE__ */ jsx166(Trash23, { className: "h-4 w-4 mr-2" }),
14233
+ "Delete"
14234
+ ]
14235
+ }
14236
+ )
14237
+ ] })
14238
+ ] })
14239
+ ] }),
14240
+ /* @__PURE__ */ jsx166(AlertDialog, { open: showDeleteConfirm, onOpenChange: setShowDeleteConfirm, children: /* @__PURE__ */ jsxs100(AlertDialogContent, { children: [
14241
+ /* @__PURE__ */ jsxs100(AlertDialogHeader, { children: [
14242
+ /* @__PURE__ */ jsx166(AlertDialogTitle, { children: "Delete OAuth Application?" }),
14243
+ /* @__PURE__ */ jsxs100(AlertDialogDescription, { children: [
14244
+ 'This will permanently delete "',
14245
+ client.name,
14246
+ '" and revoke all access tokens. This action cannot be undone.'
14247
+ ] })
14248
+ ] }),
14249
+ /* @__PURE__ */ jsxs100(AlertDialogFooter, { children: [
14250
+ /* @__PURE__ */ jsx166(AlertDialogCancel, { disabled: isDeleting, children: "Cancel" }),
14251
+ /* @__PURE__ */ jsx166(
14252
+ AlertDialogAction,
14253
+ {
14254
+ onClick: handleDelete,
14255
+ disabled: isDeleting,
14256
+ className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
14257
+ children: isDeleting ? "Deleting..." : "Delete"
14258
+ }
14259
+ )
14260
+ ] })
14261
+ ] }) }),
14262
+ /* @__PURE__ */ jsx166(AlertDialog, { open: showRegenerateConfirm, onOpenChange: setShowRegenerateConfirm, children: /* @__PURE__ */ jsxs100(AlertDialogContent, { children: [
14263
+ /* @__PURE__ */ jsxs100(AlertDialogHeader, { children: [
14264
+ /* @__PURE__ */ jsx166(AlertDialogTitle, { children: "Regenerate Client Secret?" }),
14265
+ /* @__PURE__ */ jsx166(AlertDialogDescription, { children: "This will generate a new client secret and invalidate the old one. All existing tokens will be revoked. You will need to update your application with the new secret." })
14266
+ ] }),
14267
+ /* @__PURE__ */ jsxs100(AlertDialogFooter, { children: [
14268
+ /* @__PURE__ */ jsx166(AlertDialogCancel, { disabled: isRegenerating, children: "Cancel" }),
14269
+ /* @__PURE__ */ jsx166(AlertDialogAction, { onClick: handleRegenerateSecret, disabled: isRegenerating, children: isRegenerating ? "Regenerating..." : "Regenerate" })
14270
+ ] })
14271
+ ] }) })
14272
+ ] });
14273
+ }
14274
+ __name(OAuthClientDetail, "OAuthClientDetail");
14275
+
14276
+ // src/features/oauth/components/consent/OAuthConsentHeader.tsx
14277
+ import { Shield } from "lucide-react";
14278
+ import { jsx as jsx167, jsxs as jsxs101 } from "react/jsx-runtime";
14279
+ function OAuthConsentHeader({
14280
+ client,
14281
+ logoUrl,
14282
+ appName = "Only35"
14283
+ }) {
14284
+ return /* @__PURE__ */ jsxs101("div", { className: "text-center space-y-4", children: [
14285
+ /* @__PURE__ */ jsx167("div", { className: "flex justify-center", children: logoUrl ? /* @__PURE__ */ jsx167(
14286
+ "img",
14287
+ {
14288
+ src: logoUrl,
14289
+ alt: appName,
14290
+ className: "h-12 w-auto"
14291
+ }
14292
+ ) : /* @__PURE__ */ jsx167("div", { className: "h-12 w-12 rounded-full bg-primary flex items-center justify-center", children: /* @__PURE__ */ jsx167(Shield, { className: "h-6 w-6 text-primary-foreground" }) }) }),
14293
+ /* @__PURE__ */ jsxs101("div", { className: "space-y-2", children: [
14294
+ /* @__PURE__ */ jsxs101("h1", { className: "text-2xl font-bold", children: [
14295
+ "Authorize ",
14296
+ client.name
14297
+ ] }),
14298
+ /* @__PURE__ */ jsxs101("p", { className: "text-muted-foreground", children: [
14299
+ /* @__PURE__ */ jsx167("span", { className: "font-medium text-foreground", children: client.name }),
14300
+ " ",
14301
+ "wants to access your ",
14302
+ appName,
14303
+ " account"
14304
+ ] })
14305
+ ] })
14306
+ ] });
14307
+ }
14308
+ __name(OAuthConsentHeader, "OAuthConsentHeader");
14309
+
14310
+ // src/features/oauth/components/consent/OAuthScopeList.tsx
14311
+ import {
14312
+ Eye,
14313
+ Pencil as Pencil3,
14314
+ Image as Image16,
14315
+ Upload,
14316
+ Film,
14317
+ FolderPlus,
14318
+ User,
14319
+ Shield as Shield2
14320
+ } from "lucide-react";
14321
+ import { jsx as jsx168, jsxs as jsxs102 } from "react/jsx-runtime";
14322
+ var SCOPE_ICONS = {
14323
+ eye: Eye,
14324
+ pencil: Pencil3,
14325
+ image: Image16,
14326
+ upload: Upload,
14327
+ film: Film,
14328
+ "folder-plus": FolderPlus,
14329
+ user: User,
14330
+ shield: Shield2
14331
+ };
14332
+ function OAuthScopeList({ scopes }) {
14333
+ if (scopes.length === 0) {
14334
+ return null;
14335
+ }
14336
+ return /* @__PURE__ */ jsxs102("div", { className: "space-y-3", children: [
14337
+ /* @__PURE__ */ jsx168("h2", { className: "text-sm font-medium text-muted-foreground uppercase tracking-wide", children: "This will allow the application to:" }),
14338
+ /* @__PURE__ */ jsx168("ul", { className: "space-y-3", children: scopes.map((scope) => {
14339
+ const IconComponent = scope.icon ? SCOPE_ICONS[scope.icon] : Eye;
14340
+ return /* @__PURE__ */ jsxs102(
14341
+ "li",
14342
+ {
14343
+ className: "flex items-start gap-3 p-3 rounded-lg bg-muted/50",
14344
+ children: [
14345
+ /* @__PURE__ */ jsx168("div", { className: "flex-shrink-0 mt-0.5", children: /* @__PURE__ */ jsx168("div", { className: "h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center", children: IconComponent && /* @__PURE__ */ jsx168(IconComponent, { className: "h-4 w-4 text-primary" }) }) }),
14346
+ /* @__PURE__ */ jsxs102("div", { className: "flex-1", children: [
14347
+ /* @__PURE__ */ jsx168("p", { className: "font-medium", children: scope.name }),
14348
+ /* @__PURE__ */ jsx168("p", { className: "text-sm text-muted-foreground", children: scope.description })
14349
+ ] })
14350
+ ]
14351
+ },
14352
+ scope.scope
14353
+ );
14354
+ }) })
14355
+ ] });
14356
+ }
14357
+ __name(OAuthScopeList, "OAuthScopeList");
14358
+
14359
+ // src/features/oauth/components/consent/OAuthConsentActions.tsx
14360
+ import { jsx as jsx169, jsxs as jsxs103 } from "react/jsx-runtime";
14361
+ function OAuthConsentActions({
14362
+ onApprove,
14363
+ onDeny,
14364
+ isLoading = false
14365
+ }) {
14366
+ return /* @__PURE__ */ jsxs103("div", { className: "flex flex-col sm:flex-row gap-3", children: [
14367
+ /* @__PURE__ */ jsx169(
14368
+ Button,
14369
+ {
14370
+ variant: "outline",
14371
+ onClick: onDeny,
14372
+ disabled: isLoading,
14373
+ className: "flex-1",
14374
+ children: "Deny"
14375
+ }
14376
+ ),
14377
+ /* @__PURE__ */ jsx169(
14378
+ Button,
14379
+ {
14380
+ onClick: onApprove,
14381
+ disabled: isLoading,
14382
+ className: "flex-1",
14383
+ children: isLoading ? "Authorizing..." : "Authorize"
14384
+ }
14385
+ )
14386
+ ] });
14387
+ }
14388
+ __name(OAuthConsentActions, "OAuthConsentActions");
14389
+
14390
+ // src/features/oauth/components/consent/OAuthConsentScreen.tsx
14391
+ import { ExternalLink as ExternalLink2, AlertTriangle as AlertTriangle2, Loader2 as Loader22 } from "lucide-react";
14392
+ import { jsx as jsx170, jsxs as jsxs104 } from "react/jsx-runtime";
14393
+ function OAuthConsentScreen({
14394
+ params,
14395
+ logoUrl,
14396
+ appName = "Only35",
14397
+ termsUrl = "/terms",
14398
+ privacyUrl = "/privacy"
14399
+ }) {
14400
+ const { clientInfo, isLoading, error, approve, deny, isSubmitting } = useOAuthConsent(params);
14401
+ if (isLoading) {
14402
+ return /* @__PURE__ */ jsx170("div", { className: "min-h-screen flex items-center justify-center p-4", children: /* @__PURE__ */ jsx170(Card, { className: "w-full max-w-md", children: /* @__PURE__ */ jsxs104(CardContent, { className: "flex flex-col items-center justify-center py-12", children: [
14403
+ /* @__PURE__ */ jsx170(Loader22, { className: "h-8 w-8 animate-spin text-muted-foreground" }),
14404
+ /* @__PURE__ */ jsx170("p", { className: "mt-4 text-muted-foreground", children: "Loading authorization request..." })
14405
+ ] }) }) });
14406
+ }
14407
+ if (error || !clientInfo) {
14408
+ return /* @__PURE__ */ jsx170("div", { className: "min-h-screen flex items-center justify-center p-4", children: /* @__PURE__ */ jsx170(Card, { className: "w-full max-w-md", children: /* @__PURE__ */ jsx170(CardContent, { className: "py-8", children: /* @__PURE__ */ jsxs104(Alert, { variant: "destructive", children: [
14409
+ /* @__PURE__ */ jsx170(AlertTriangle2, { className: "h-4 w-4" }),
14410
+ /* @__PURE__ */ jsx170(AlertDescription, { children: error?.message || "Invalid authorization request. Please try again." })
14411
+ ] }) }) }) });
14412
+ }
14413
+ const { client, scopes } = clientInfo;
14414
+ return /* @__PURE__ */ jsx170("div", { className: "min-h-screen flex items-center justify-center p-4 bg-muted/30", children: /* @__PURE__ */ jsxs104(Card, { className: "w-full max-w-md", children: [
14415
+ /* @__PURE__ */ jsxs104(CardContent, { className: "pt-6 space-y-6", children: [
14416
+ /* @__PURE__ */ jsx170(
14417
+ OAuthConsentHeader,
14418
+ {
14419
+ client,
14420
+ logoUrl,
14421
+ appName
14422
+ }
14423
+ ),
14424
+ /* @__PURE__ */ jsx170(Separator, {}),
14425
+ /* @__PURE__ */ jsx170(OAuthScopeList, { scopes }),
14426
+ /* @__PURE__ */ jsx170(Separator, {}),
14427
+ /* @__PURE__ */ jsxs104("div", { className: "flex items-start gap-2 text-sm text-muted-foreground", children: [
14428
+ /* @__PURE__ */ jsx170(ExternalLink2, { className: "h-4 w-4 mt-0.5 flex-shrink-0" }),
14429
+ /* @__PURE__ */ jsxs104("div", { children: [
14430
+ /* @__PURE__ */ jsx170("span", { children: "Authorizing will redirect you to:" }),
14431
+ /* @__PURE__ */ jsx170("p", { className: "font-mono text-xs mt-1 break-all", children: params.redirectUri })
14432
+ ] })
14433
+ ] }),
14434
+ /* @__PURE__ */ jsx170(
14435
+ OAuthConsentActions,
14436
+ {
14437
+ onApprove: approve,
14438
+ onDeny: deny,
14439
+ isLoading: isSubmitting
14440
+ }
14441
+ )
14442
+ ] }),
14443
+ /* @__PURE__ */ jsx170(CardFooter, { className: "justify-center", children: /* @__PURE__ */ jsxs104("p", { className: "text-xs text-center text-muted-foreground", children: [
14444
+ "By authorizing, you agree to the app's",
14445
+ " ",
14446
+ /* @__PURE__ */ jsx170("a", { href: termsUrl, className: "underline hover:text-foreground", target: "_blank", rel: "noopener", children: "Terms of Service" }),
14447
+ " ",
14448
+ "and",
14449
+ " ",
14450
+ /* @__PURE__ */ jsx170("a", { href: privacyUrl, className: "underline hover:text-foreground", target: "_blank", rel: "noopener", children: "Privacy Policy" }),
14451
+ "."
14452
+ ] }) })
14453
+ ] }) });
14454
+ }
14455
+ __name(OAuthConsentScreen, "OAuthConsentScreen");
14456
+
13278
14457
  export {
13279
14458
  JsonApiProvider,
13280
14459
  useJsonApiGet,
@@ -13650,6 +14829,18 @@ export {
13650
14829
  useRoleTableStructure,
13651
14830
  RolesList,
13652
14831
  UserRolesList,
14832
+ OAuthRedirectUriInput,
14833
+ OAuthScopeSelector,
14834
+ OAuthClientSecretDisplay,
14835
+ OAuthClientCard,
14836
+ OAuthClientList,
14837
+ OAuthClientForm,
14838
+ OAuthClientDetail,
14839
+ OAuthConsentHeader,
14840
+ OAuthScopeList,
14841
+ OAuthConsentActions,
14842
+ useOAuthConsent,
14843
+ OAuthConsentScreen,
13653
14844
  AddUserToRole,
13654
14845
  UserAvatarEditor,
13655
14846
  UserDeleter,
@@ -13672,6 +14863,8 @@ export {
13672
14863
  useSocket,
13673
14864
  useUserSearch,
13674
14865
  useUserTableStructure,
13675
- useContentTableStructure
14866
+ useContentTableStructure,
14867
+ useOAuthClients,
14868
+ useOAuthClient
13676
14869
  };
13677
- //# sourceMappingURL=chunk-H5JZ5E7M.mjs.map
14870
+ //# sourceMappingURL=chunk-6BDOZDZ3.mjs.map