diff --git a/.github/workflows/web-sdk.yml b/.github/workflows/web-sdk.yml index cd70a3af1..be351b75c 100644 --- a/.github/workflows/web-sdk.yml +++ b/.github/workflows/web-sdk.yml @@ -45,19 +45,19 @@ jobs: cat << EOF > "$HOME/.npmrc" //registry.npmjs.org/:_authToken=$NPM_TOKEN EOF - working-directory: ./web/lib + working-directory: ./web/sdk env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Bump Package Version run: npm run bump-version - working-directory: ./web/lib + working-directory: ./web/sdk env: GIT_REFNAME: ${{ github.ref_name }} - name: Run Semantic Release 🚀 run: npm run release:ci - working-directory: ./web/lib + working-directory: ./web/sdk env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/web/apps/admin/src/pages/organizations/details/index.tsx b/web/apps/admin/src/pages/organizations/details/index.tsx index dc6acadfe..a942c7e9f 100644 --- a/web/apps/admin/src/pages/organizations/details/index.tsx +++ b/web/apps/admin/src/pages/organizations/details/index.tsx @@ -1,272 +1,65 @@ -import { useEffect, useMemo, useState } from "react"; import { - useQuery, - createConnectQueryKey, - useTransport, -} from "@connectrpc/connect-query"; -import { Outlet, useParams } from "react-router-dom"; + OrganizationDetails, +} from "@raystack/frontier/admin"; +import { useCallback, useContext, useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import { AppContext } from "~/contexts/App"; +import { clients } from "~/connect/clients"; +import { exportCsvFromStream } from "~/utils/helper"; + +const adminClient = clients.admin({ useBinary: true }); + +async function loadCountries(): Promise { + const data = await import("~/assets/data/countries.json"); + return (data.default as { name: string }[]).map((c) => c.name); +} + +export function OrganizationDetailsPage() { + const { organizationId } = useParams<{ organizationId: string }>(); + const { config } = useContext(AppContext); + const [countries, setCountries] = useState([]); -import { OrganizationDetailsLayout } from "./layout"; -import { ORG_NAMESPACE } from "./types"; -import { OrganizationContext } from "./contexts/organization-context"; -import { - FrontierServiceQueries, - GetOrganizationKycResponseSchema, - type Organization, - type User, -} from "@raystack/proton/frontier"; -import { queryClient } from "~/contexts/ConnectProvider"; -import { create } from "@bufbuild/protobuf"; - -export const OrganizationDetails = () => { - const [isSearchVisible, setIsSearchVisible] = useState(false); - const [searchQuery, setSearchQuery] = useState(""); - - const { organizationId } = useParams(); - const transport = useTransport(); - - // Use Connect RPC for fetching organization - const { - data: organization, - isLoading: isOrganizationLoading, - error: organizationError, - } = useQuery( - FrontierServiceQueries.getOrganization, - { id: organizationId }, - { - enabled: !!organizationId, - select: (data) => data?.organization, - }, - ); - - const getOrganizationQueryKey = [ - FrontierServiceQueries.getOrganization, - { id: organizationId }, - ]; - - async function updateOrganization(org: Organization) { - queryClient.setQueryData(getOrganizationQueryKey, { organization: org }); - } - - // Fetch KYC details - const { - data: kycData, - isLoading: isKYCLoading, - error: kycError, - } = useQuery( - FrontierServiceQueries.getOrganizationKyc, - { orgId: organizationId || "" }, - { - enabled: !!organizationId, - }, - ); - - const kycDetails = useMemo(() => kycData?.organizationKyc, [kycData]); + useEffect(() => { + loadCountries().then(setCountries); + }, []); - function updateKYCDetails(kyc: typeof kycDetails) { + const onExportMembers = useCallback(async () => { if (!organizationId) return; - queryClient.setQueryData( - createConnectQueryKey({ - schema: FrontierServiceQueries.getOrganizationKyc, - transport, - input: { orgId: organizationId }, - cardinality: "finite", - }), - create(GetOrganizationKycResponseSchema, { organizationKyc: kyc }), + await exportCsvFromStream( + adminClient.exportOrganizationUsers, + { id: organizationId }, + "organization-members.csv", ); - } + }, [organizationId]); - // Fetch default roles - const { - data: defaultRoles = [], - isLoading: isDefaultRolesLoading, - error: defaultRolesError, - } = useQuery( - FrontierServiceQueries.listRoles, - { scopes: [ORG_NAMESPACE] }, - { - enabled: !!organizationId, - select: (data) => data?.roles || [], - }, - ); - - // Fetch organization-specific roles - const { - data: organizationRoles = [], - isLoading: isOrgRolesLoading, - error: orgRolesError, - } = useQuery( - FrontierServiceQueries.listOrganizationRoles, - { orgId: organizationId || "", scopes: [ORG_NAMESPACE] }, - { - enabled: !!organizationId, - select: (data) => data?.roles || [], - }, - ); - - const roles = [...defaultRoles, ...organizationRoles]; - - // Fetch organization members - const { - data: orgMembersMap = {}, - isLoading: isOrgMembersMapLoading, - error: orgMembersError, - } = useQuery( - FrontierServiceQueries.listOrganizationUsers, - { id: organizationId || "" }, - { - enabled: !!organizationId, - select: (data) => { - const users = data?.users || []; - return users.reduce( - (acc, user) => { - const id = user.id || ""; - acc[id] = user; - return acc; - }, - {} as Record, - ); - }, - }, - ); - - // Fetch billing accounts list - const { data: firstBillingAccountId = "", error: billingAccountsError } = - useQuery( - FrontierServiceQueries.listBillingAccounts, - { orgId: organizationId || "" }, - { - enabled: !!organizationId, - select: (data) => data?.billingAccounts?.[0]?.id || "", - }, + const onExportProjects = useCallback(async () => { + if (!organizationId) return; + await exportCsvFromStream( + adminClient.exportOrganizationProjects, + { id: organizationId }, + "organization-projects.csv", ); + }, [organizationId]); - // Fetch billing account details - const { - data: billingAccountData, - isLoading: isBillingAccountLoading, - error: billingAccountError, - refetch: fetchBillingAccountDetails, - } = useQuery( - FrontierServiceQueries.getBillingAccount, - { - orgId: organizationId || "", - id: firstBillingAccountId, - withBillingDetails: true, - }, - { - enabled: !!organizationId && !!firstBillingAccountId, - select: (data) => ({ - billingAccount: data?.billingAccount, - billingAccountDetails: data?.billingDetails, - }), - }, - ); - - const billingAccount = billingAccountData?.billingAccount; - const billingAccountDetails = billingAccountData?.billingAccountDetails; - - // Fetch billing balance - const { - data: tokenBalance = "0", - isLoading: isTokenBalanceLoading, - error: tokenBalanceError, - refetch: fetchTokenBalance, - } = useQuery( - FrontierServiceQueries.getBillingBalance, - { - orgId: organizationId || "", - id: firstBillingAccountId, - }, - { - enabled: !!organizationId && !!firstBillingAccountId, - select: (data) => String(data?.balance?.amount || "0"), - }, - ); - - // Error handling - useEffect(() => { - if (organizationError) { - console.error("Failed to fetch organization:", organizationError); - } - if (kycError) { - console.error("Failed to fetch KYC details:", kycError); - } - if (defaultRolesError) { - console.error("Failed to fetch default roles:", defaultRolesError); - } - if (orgRolesError) { - console.error("Failed to fetch organization roles:", orgRolesError); - } - if (orgMembersError) { - console.error("Failed to fetch organization members:", orgMembersError); - } - if (billingAccountsError) { - console.error("Failed to fetch billing accounts:", billingAccountsError); - } - if (billingAccountError) { - console.error( - "Failed to fetch billing account details:", - billingAccountError, - ); - } - if (tokenBalanceError) { - console.error("Failed to fetch token balance:", tokenBalanceError); - } - }, [ - organizationError, - kycError, - defaultRolesError, - orgRolesError, - orgMembersError, - billingAccountsError, - billingAccountError, - tokenBalanceError, - ]); + const onExportTokens = useCallback(async () => { + if (!organizationId) return; + await exportCsvFromStream( + adminClient.exportOrganizationTokens, + { id: organizationId }, + "organization-tokens.csv", + ); + }, [organizationId]); - const isLoading = - isOrganizationLoading || - isDefaultRolesLoading || - isOrgRolesLoading || - isBillingAccountLoading; return ( - - - {organization?.id ? ( - - ) : null} - - + ); -}; +} diff --git a/web/apps/admin/src/pages/organizations/list/index.tsx b/web/apps/admin/src/pages/organizations/list/index.tsx index b73edb36e..f16c4f76a 100644 --- a/web/apps/admin/src/pages/organizations/list/index.tsx +++ b/web/apps/admin/src/pages/organizations/list/index.tsx @@ -1,200 +1,49 @@ -import { DataTable, EmptyState, Flex, type DataTableQuery, type DataTableSort } from "@raystack/apsara"; -import { OrganizationIcon } from "@raystack/apsara/icons"; -import { useEffect, useState } from "react"; -import { OrganizationsNavabar } from "./navbar"; -import styles from "./list.module.css"; -import { getColumns } from "./columns"; -import { useInfiniteQuery, useQuery } from "@connectrpc/connect-query"; import { - AdminServiceQueries, - FrontierServiceQueries, - SearchOrganizationsResponse_OrganizationResult, - type Plan, - ListPlansRequestSchema, -} from "@raystack/proton/frontier"; -import { create } from "@bufbuild/protobuf"; - -import { useNavigate } from "react-router-dom"; -import PageTitle from "~/components/page-title"; -import { CreateOrganizationPanel } from "./create"; -import { - getConnectNextPageParam, - getGroupCountMapFromFirstPage, - DEFAULT_PAGE_SIZE, - transformDataTableQueryToRQLRequest, + OrganizationList, } from "@raystack/frontier/admin"; -import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; -import { useDebouncedState } from "@raystack/apsara/hooks"; - -const NoOrganizations = () => { - return ( - } - /> - ); -}; - -const DEFAULT_SORT: DataTableSort = { name: "created_at", order: "desc" }; -const INITIAL_QUERY: DataTableQuery = { - offset: 0, - limit: DEFAULT_PAGE_SIZE, -}; - -export const OrganizationList = () => { - const [showCreatePanel, setShowCreatePanel] = useState(false); - - const [tableQuery, setTableQuery] = useDebouncedState( - INITIAL_QUERY, - 200, - ); - - // Transform the DataTableQuery to RQLRequest format - const query = transformDataTableQueryToRQLRequest(tableQuery, { - fieldNameMapping: { - createdBy: "created_by", - planName: "plan_name", - subscriptionCycleEndAt: "subscription_cycle_end_at", - paymentMode: "payment_mode", - subscriptionState: "subscription_state", - createdAt: "created_at", - }, - }); - - const { - data: infiniteData, - isLoading, - isFetchingNextPage, - fetchNextPage, - error, - isError, - } = useInfiniteQuery( - AdminServiceQueries.searchOrganizations, - { query: query }, - { - pageParamKey: "query", - getNextPageParam: lastPage => - getConnectNextPageParam(lastPage, { query: query }, "organizations"), - staleTime: 0, - refetchOnWindowFocus: false, - retry: 1, - retryDelay: 1000, - }, - ); - - const { data: plans = [], isLoading: isPlansLoading, error: plansError } = useQuery( - FrontierServiceQueries.listPlans, - create(ListPlansRequestSchema, {}), - { - select: (data) => data?.plans || [], - }, - ); - - // Log error if it occurs - useEffect(() => { - if (plansError) { - console.error("Failed to fetch plans:", plansError); - } - }, [plansError]); - - const data = - infiniteData?.pages?.flatMap(page => page?.organizations || []) || []; - - const groupCountMap = infiniteData - ? getGroupCountMapFromFirstPage(infiniteData) - : {}; +import { useCallback, useContext, useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { AppContext } from "~/contexts/App"; +import { clients } from "~/connect/clients"; +import { exportCsvFromStream } from "~/utils/helper"; - const onTableQueryChange = (newQuery: DataTableQuery) => { - setTableQuery({ - ...newQuery, - offset: 0, - limit: newQuery.limit || DEFAULT_PAGE_SIZE, - }); - }; +const adminClient = clients.admin({ useBinary: true }); - const handleLoadMore = async () => { - try { - await fetchNextPage(); - } catch (error) { - console.error("Error loading more organizations:", error); - } - }; +async function loadCountries(): Promise { + const data = await import("~/assets/data/countries.json"); + return (data.default as { name: string }[]).map((c) => c.name); +} +export function OrganizationListPage() { const navigate = useNavigate(); + const { config } = useContext(AppContext); + const [countries, setCountries] = useState([]); - function closeCreateOrgPanel() { - setShowCreatePanel(false); - } - - function openCreateOrgPanel() { - setShowCreatePanel(true); - } - - const columns = getColumns({ plans, groupCountMap }); + useEffect(() => { + loadCountries().then(setCountries); + }, []); - const loading = isLoading || isPlansLoading || isFetchingNextPage; + const onNavigateToOrg = useCallback( + (id: string) => navigate(`/organizations/${id}`), + [navigate], + ); - if (isError) { - return ( - <> - - } - heading="Error Loading Organizations" - subHeading={ - error?.message || - "Something went wrong while loading organizations. Please try again." - } - /> - + const onExportCsv = useCallback(async () => { + await exportCsvFromStream( + adminClient.exportOrganizations, + {}, + "organizations.csv", ); - } - - const tableClassName = - data.length || loading ? styles["table"] : styles["table-empty"]; + }, []); - function onRowClick(row: SearchOrganizationsResponse_OrganizationResult) { - navigate(`/organizations/${row.id}`); - } return ( - <> - {showCreatePanel ? ( - - ) : null} - - - - - - } - rowHeight={48} - groupHeaderHeight={48} - /> - - - + ); -}; +} diff --git a/web/apps/admin/src/pages/users/UsersPage.tsx b/web/apps/admin/src/pages/users/UsersPage.tsx new file mode 100644 index 000000000..3561b8f6e --- /dev/null +++ b/web/apps/admin/src/pages/users/UsersPage.tsx @@ -0,0 +1,32 @@ +import { UsersView } from "@raystack/frontier/admin"; +import { useCallback } from "react"; +import { useParams, useNavigate } from "react-router-dom"; +import { clients } from "~/connect/clients"; +import { exportCsvFromStream } from "~/utils/helper"; + +const adminClient = clients.admin({ useBinary: true }); + +export function UsersPage() { + const { userId } = useParams(); + const navigate = useNavigate(); + + const onExportUsers = useCallback(async () => { + await exportCsvFromStream(adminClient.exportUsers, {}, "users.csv"); + }, []); + + const onNavigateToUser = useCallback( + (id: string) => { + navigate(`/users/${id}/security`); + }, + [navigate], + ); + + return ( + navigate("/users")} + onExportUsers={onExportUsers} + onNavigateToUser={onNavigateToUser} + /> + ); +} diff --git a/web/apps/admin/src/routes.tsx b/web/apps/admin/src/routes.tsx index 47c5b1190..8de23a810 100644 --- a/web/apps/admin/src/routes.tsx +++ b/web/apps/admin/src/routes.tsx @@ -22,18 +22,18 @@ import { AdminsPage } from "./pages/admins/AdminsPage"; import { WebhooksPage } from "./pages/webhooks/WebhooksPage"; import AuthLayout from "./layout/auth"; -import { OrganizationList } from "./pages/organizations/list"; -import { OrganizationDetails } from "./pages/organizations/details"; -import { OrganizationSecurity } from "./pages/organizations/details/security"; -import { OrganizationMembersPage } from "./pages/organizations/details/members"; -import { OrganizationProjectssPage } from "./pages/organizations/details/projects"; -import { OrganizationInvoicesPage } from "./pages/organizations/details/invoices"; -import { OrganizationTokensPage } from "./pages/organizations/details/tokens"; -import { OrganizationApisPage } from "./pages/organizations/details/apis"; - -import { UsersList } from "./pages/users/list"; -import { UserDetails } from "./pages/users/details"; -import { UserDetailsSecurityPage } from "./pages/users/details/security"; +import { OrganizationListPage } from "./pages/organizations/list"; +import { OrganizationDetailsPage } from "./pages/organizations/details"; +import { + OrganizationSecurity, + OrganizationMembersPage, + OrganizationProjectssPage, + OrganizationInvoicesPage, + OrganizationTokensPage, + OrganizationApisPage, +} from "@raystack/frontier/admin"; + +import { UsersPage } from "./pages/users/UsersPage"; import { InvoicesPage } from "./pages/invoices/InvoicesPage"; import { AuditLogsPage } from "./pages/audit-logs/AuditLogsPage"; @@ -59,10 +59,10 @@ export default memo(function AppRoutes() { }> } /> - } /> + } /> }> + element={}> } /> } /> } /> @@ -71,10 +71,9 @@ export default memo(function AppRoutes() { } /> } /> - } /> - }> - } /> - } /> + }> + } /> + } /> } /> diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 7d4bbb8cc..6709650da 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -59,7 +59,7 @@ importers: version: 0.56.2(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@raystack/frontier': specifier: workspace:^ - version: link:../../lib + version: link:../../sdk '@raystack/proton': specifier: 0.1.0-b1687af73f994fa9612a023c850aa97c35735af8 version: 0.1.0-b1687af73f994fa9612a023c850aa97c35735af8(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -168,7 +168,7 @@ importers: version: 0.52.0(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@raystack/frontier': specifier: workspace:^ - version: link:../../lib + version: link:../../sdk react: specifier: ^18.3.1 version: 18.3.1 @@ -207,7 +207,7 @@ importers: specifier: ^7.0.6 version: 7.3.1(@types/node@20.19.31)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) - lib: + sdk: dependencies: '@bufbuild/protobuf': specifier: ^2.11.0 @@ -366,6 +366,9 @@ importers: typescript: specifier: ^5.8.3 version: 5.9.3 + usehooks-ts: + specifier: ^3.1.1 + version: 3.1.1(react@18.3.1) zod: specifier: ^3.22.3 version: 3.25.76 diff --git a/web/pnpm-workspace.yaml b/web/pnpm-workspace.yaml index 4a9a90be7..9702da1a4 100644 --- a/web/pnpm-workspace.yaml +++ b/web/pnpm-workspace.yaml @@ -1,4 +1,4 @@ packages: - "apps/*" - "tools/*" - - "lib" \ No newline at end of file + - "sdk" \ No newline at end of file diff --git a/web/lib/.release-it.json b/web/sdk/.release-it.json similarity index 100% rename from web/lib/.release-it.json rename to web/sdk/.release-it.json diff --git a/web/lib/CHANGELOG.md b/web/sdk/CHANGELOG.md similarity index 100% rename from web/lib/CHANGELOG.md rename to web/sdk/CHANGELOG.md diff --git a/web/lib/README.md b/web/sdk/README.md similarity index 100% rename from web/lib/README.md rename to web/sdk/README.md diff --git a/web/lib/admin/assets/icons/CpuChipIcon.tsx b/web/sdk/admin/assets/icons/CpuChipIcon.tsx similarity index 100% rename from web/lib/admin/assets/icons/CpuChipIcon.tsx rename to web/sdk/admin/assets/icons/CpuChipIcon.tsx diff --git a/web/lib/admin/assets/icons/InvoicesIcon.tsx b/web/sdk/admin/assets/icons/InvoicesIcon.tsx similarity index 100% rename from web/lib/admin/assets/icons/InvoicesIcon.tsx rename to web/sdk/admin/assets/icons/InvoicesIcon.tsx diff --git a/web/lib/admin/assets/icons/JsonIcon.tsx b/web/sdk/admin/assets/icons/JsonIcon.tsx similarity index 100% rename from web/lib/admin/assets/icons/JsonIcon.tsx rename to web/sdk/admin/assets/icons/JsonIcon.tsx diff --git a/web/lib/admin/assets/icons/KeyIcon.tsx b/web/sdk/admin/assets/icons/KeyIcon.tsx similarity index 100% rename from web/lib/admin/assets/icons/KeyIcon.tsx rename to web/sdk/admin/assets/icons/KeyIcon.tsx diff --git a/web/lib/admin/assets/icons/MapIcon.tsx b/web/sdk/admin/assets/icons/MapIcon.tsx similarity index 100% rename from web/lib/admin/assets/icons/MapIcon.tsx rename to web/sdk/admin/assets/icons/MapIcon.tsx diff --git a/web/sdk/admin/assets/icons/UsersIcon.tsx b/web/sdk/admin/assets/icons/UsersIcon.tsx new file mode 100644 index 000000000..d65298bc4 --- /dev/null +++ b/web/sdk/admin/assets/icons/UsersIcon.tsx @@ -0,0 +1,24 @@ +import type { SVGProps } from "react"; + +export function UsersIcon(props: SVGProps) { + return ( + + + + + + ); +} + +export default UsersIcon; diff --git a/web/lib/admin/assets/images/system.jpg b/web/sdk/admin/assets/images/system.jpg similarity index 100% rename from web/lib/admin/assets/images/system.jpg rename to web/sdk/admin/assets/images/system.jpg diff --git a/web/sdk/admin/components/AssignRole.tsx b/web/sdk/admin/components/AssignRole.tsx new file mode 100644 index 000000000..7c5e37212 --- /dev/null +++ b/web/sdk/admin/components/AssignRole.tsx @@ -0,0 +1,214 @@ +import { + Button, + Checkbox, + Dialog, + Flex, + Label, + Text, + toast, +} from "@raystack/apsara"; +import { useCallback } from "react"; +import type { + SearchOrganizationUsersResponse_OrganizationUser, + Role, + Policy, +} from "@raystack/proton/frontier"; +import { + FrontierService, + FrontierServiceQueries, + ListPoliciesRequestSchema, + DeletePolicyRequestSchema, + CreatePolicyRequestSchema, +} from "@raystack/proton/frontier"; +import { create } from "@bufbuild/protobuf"; +import { useMutation, useTransport } from "@connectrpc/connect-query"; +import { createClient } from "@connectrpc/connect"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; +import { SCOPES } from "../utils/constants"; + +interface AssignRoleProps { + organizationId: string; + roles: Role[]; + user?: SearchOrganizationUsersResponse_OrganizationUser; + onRoleUpdate: () => void; + onClose: () => void; +} + +const formSchema = z.object({ + roleIds: z.instanceof(Set).refine((set) => set.size > 0, { + message: "At least one role must be selected", + }), +}); + +type FormData = z.infer; + +export const AssignRole = ({ + roles = [], + user, + organizationId, + onRoleUpdate, + onClose, +}: AssignRoleProps) => { + const transport = useTransport(); + + const { + handleSubmit, + watch, + setValue, + formState: { isSubmitting, errors }, + } = useForm({ + defaultValues: { + roleIds: new Set(user?.roleIds || []), + }, + resolver: zodResolver(formSchema), + }); + + const { mutateAsync: deletePolicy } = useMutation( + FrontierServiceQueries.deletePolicy, + ); + + const { mutateAsync: createPolicy } = useMutation( + FrontierServiceQueries.createPolicy, + ); + + const roleIds = watch("roleIds"); + + function onCheckedChange(value: boolean | string, roleId?: string) { + if (!roleId) return; + const currentRoles = new Set(roleIds); + + if (value) { + currentRoles.add(roleId); + } else { + currentRoles.delete(roleId); + } + + setValue("roleIds", currentRoles); + } + + const checkRole = useCallback( + (roleId?: string) => { + if (!roleId) return false; + return roleIds?.has(roleId) || false; + }, + [roleIds], + ); + + const onSubmit = async (data: FormData) => { + try { + const client = createClient(FrontierService, transport); + const policiesResp = await client.listPolicies( + create(ListPoliciesRequestSchema, { + orgId: organizationId, + userId: user?.id, + }), + ); + const policies = policiesResp.policies || []; + + const removedRolesPolicies = policies.filter( + (policy: Policy) => !(policy.roleId && data.roleIds.has(policy.roleId)), + ); + await Promise.all( + removedRolesPolicies.map((policy: Policy) => + deletePolicy( + create(DeletePolicyRequestSchema, { id: policy.id || "" }), + ), + ), + ); + + const resource = `${SCOPES.ORG}:${organizationId}`; + const principal = `${SCOPES.USER}:${user?.id}`; + + const assignedRolesArr = Array.from(data.roleIds); + await Promise.all( + assignedRolesArr.map((roleId) => + createPolicy( + create(CreatePolicyRequestSchema, { + body: { + roleId, + resource, + principal, + }, + }), + ), + ), + ); + + if (onRoleUpdate) { + onRoleUpdate(); + } + + toast.success("Role assigned successfully"); + } catch (error) { + toast.error("Failed to assign role"); + console.error(error); + } + }; + + return ( + + + + Assign Role + + +
+ + + + Taking this action may result in changes in the role which might + lead to changes in access of the user. + +
+ + {roles.map((role) => { + const htmlId = `role-${role.id}`; + const checked = checkRole(role.id); + return ( + + + onCheckedChange(value, role.id) + } + /> + + + ); + })} + {errors.roleIds && ( + {errors.roleIds.message} + )} + +
+
+
+ + + + + + +
+
+
+ ); +}; diff --git a/web/lib/admin/components/CustomField.tsx b/web/sdk/admin/components/CustomField.tsx similarity index 100% rename from web/lib/admin/components/CustomField.tsx rename to web/sdk/admin/components/CustomField.tsx diff --git a/web/lib/admin/components/PageHeader.tsx b/web/sdk/admin/components/PageHeader.tsx similarity index 100% rename from web/lib/admin/components/PageHeader.tsx rename to web/sdk/admin/components/PageHeader.tsx diff --git a/web/lib/admin/components/PageTitle.tsx b/web/sdk/admin/components/PageTitle.tsx similarity index 100% rename from web/lib/admin/components/PageTitle.tsx rename to web/sdk/admin/components/PageTitle.tsx diff --git a/web/lib/admin/components/SheetFooter.tsx b/web/sdk/admin/components/SheetFooter.tsx similarity index 100% rename from web/lib/admin/components/SheetFooter.tsx rename to web/sdk/admin/components/SheetFooter.tsx diff --git a/web/lib/admin/components/SheetHeader.tsx b/web/sdk/admin/components/SheetHeader.tsx similarity index 100% rename from web/lib/admin/components/SheetHeader.tsx rename to web/sdk/admin/components/SheetHeader.tsx diff --git a/web/lib/admin/components/multiselect/multiselect/index.tsx b/web/sdk/admin/components/multiselect/multiselect/index.tsx similarity index 100% rename from web/lib/admin/components/multiselect/multiselect/index.tsx rename to web/sdk/admin/components/multiselect/multiselect/index.tsx diff --git a/web/lib/admin/components/page-header.module.css b/web/sdk/admin/components/page-header.module.css similarity index 100% rename from web/lib/admin/components/page-header.module.css rename to web/sdk/admin/components/page-header.module.css diff --git a/web/lib/admin/index.ts b/web/sdk/admin/index.ts similarity index 52% rename from web/lib/admin/index.ts rename to web/sdk/admin/index.ts index d28c42360..c0c7b4ece 100644 --- a/web/lib/admin/index.ts +++ b/web/sdk/admin/index.ts @@ -9,6 +9,18 @@ export { default as AdminsView } from "./views/admins"; export { default as PlansView } from "./views/plans"; export { default as WebhooksView } from "./views/webhooks/webhooks"; export { default as PreferencesView } from "./views/preferences/PreferencesView"; +export { default as UsersView } from "./views/users/UsersView"; +export { OrganizationList, type OrganizationListProps } from "./views/organizations/list"; +export { + OrganizationDetails, + type OrganizationDetailsProps, +} from "./views/organizations/details"; +export { OrganizationSecurity } from "./views/organizations/details/security"; +export { OrganizationMembersPage } from "./views/organizations/details/members"; +export { OrganizationProjectssPage } from "./views/organizations/details/projects"; +export { OrganizationInvoicesPage } from "./views/organizations/details/invoices"; +export { OrganizationTokensPage } from "./views/organizations/details/tokens"; +export { OrganizationApisPage } from "./views/organizations/details/apis"; // utils exports export { diff --git a/web/lib/admin/utils/connect-pagination.ts b/web/sdk/admin/utils/connect-pagination.ts similarity index 100% rename from web/lib/admin/utils/connect-pagination.ts rename to web/sdk/admin/utils/connect-pagination.ts diff --git a/web/lib/admin/utils/connect-timestamp.ts b/web/sdk/admin/utils/connect-timestamp.ts similarity index 70% rename from web/lib/admin/utils/connect-timestamp.ts rename to web/sdk/admin/utils/connect-timestamp.ts index f08af0d37..aef04cdb5 100644 --- a/web/lib/admin/utils/connect-timestamp.ts +++ b/web/sdk/admin/utils/connect-timestamp.ts @@ -1,10 +1,16 @@ import { timestampDate, type Timestamp } from "@bufbuild/protobuf/wkt"; +import dayjs, { type Dayjs } from "dayjs"; export function timestampToDate(timestamp?: Timestamp): Date | null { if (!timestamp) return null; return timestampDate(timestamp); } +export function timestampToDayjs(timestamp?: Timestamp): Dayjs | null { + const date = timestampToDate(timestamp); + return date ? dayjs(date) : null; +} + /** * Checks if a ConnectRPC Timestamp is the null time (0001-01-01T00:00:00Z) */ diff --git a/web/sdk/admin/utils/constants.ts b/web/sdk/admin/utils/constants.ts new file mode 100644 index 000000000..46c670d27 --- /dev/null +++ b/web/sdk/admin/utils/constants.ts @@ -0,0 +1,30 @@ +export const SCOPES = { + ORG: "app/organization", + PROJECT: "app/project", + GROUP: "app/group", + USER: "app/user", +} as const; + +export const DEFAULT_ROLES = { + ORG_MANAGER: "app_organization_manager", + ORG_OWNER: "app_organization_owner", + ORG_BILLING_MANAGER: "app_billing_manager", + ORG_VIEWER: "app_organization_viewer", + PROJECT_VIEWER: "app_project_viewer", +} as const; + +export const NULL_DATE = "0001-01-01T00:00:00Z"; + +export interface Config { + title?: string; + app_url?: string; + token_product_id?: string; + organization_types?: string[]; +} + +export const defaultConfig: Config = { + title: "Frontier Admin", + app_url: "example.com", + token_product_id: "token", + organization_types: [], +}; diff --git a/web/lib/admin/utils/helper.ts b/web/sdk/admin/utils/helper.ts similarity index 61% rename from web/lib/admin/utils/helper.ts rename to web/sdk/admin/utils/helper.ts index dd2c79f82..f19d9336d 100644 --- a/web/lib/admin/utils/helper.ts +++ b/web/sdk/admin/utils/helper.ts @@ -21,3 +21,11 @@ export function isZeroUUID(uuid: string) { export function capitalizeFirstLetter(str: string) { return str.charAt(0).toUpperCase() + str.slice(1); } + +export function converBillingAddressToString( + address?: { line1?: string; line2?: string; city?: string; state?: string; country?: string; postalCode?: string }, +) { + if (!address) return ""; + const { line1, line2, city, state, country, postalCode } = address; + return [line1, line2, city, state, country, postalCode].filter(Boolean).join(", "); +} diff --git a/web/lib/admin/utils/transform-query.ts b/web/sdk/admin/utils/transform-query.ts similarity index 100% rename from web/lib/admin/utils/transform-query.ts rename to web/sdk/admin/utils/transform-query.ts diff --git a/web/lib/admin/utils/webhook-events.ts b/web/sdk/admin/utils/webhook-events.ts similarity index 100% rename from web/lib/admin/utils/webhook-events.ts rename to web/sdk/admin/utils/webhook-events.ts diff --git a/web/lib/admin/views/admins/admins.module.css b/web/sdk/admin/views/admins/admins.module.css similarity index 100% rename from web/lib/admin/views/admins/admins.module.css rename to web/sdk/admin/views/admins/admins.module.css diff --git a/web/lib/admin/views/admins/columns.tsx b/web/sdk/admin/views/admins/columns.tsx similarity index 100% rename from web/lib/admin/views/admins/columns.tsx rename to web/sdk/admin/views/admins/columns.tsx diff --git a/web/lib/admin/views/admins/index.tsx b/web/sdk/admin/views/admins/index.tsx similarity index 100% rename from web/lib/admin/views/admins/index.tsx rename to web/sdk/admin/views/admins/index.tsx diff --git a/web/lib/admin/views/audit-logs/actor-cell.tsx b/web/sdk/admin/views/audit-logs/actor-cell.tsx similarity index 100% rename from web/lib/admin/views/audit-logs/actor-cell.tsx rename to web/sdk/admin/views/audit-logs/actor-cell.tsx diff --git a/web/lib/admin/views/audit-logs/audit-logs.module.css b/web/sdk/admin/views/audit-logs/audit-logs.module.css similarity index 100% rename from web/lib/admin/views/audit-logs/audit-logs.module.css rename to web/sdk/admin/views/audit-logs/audit-logs.module.css diff --git a/web/lib/admin/views/audit-logs/columns.tsx b/web/sdk/admin/views/audit-logs/columns.tsx similarity index 100% rename from web/lib/admin/views/audit-logs/columns.tsx rename to web/sdk/admin/views/audit-logs/columns.tsx diff --git a/web/lib/admin/views/audit-logs/index.tsx b/web/sdk/admin/views/audit-logs/index.tsx similarity index 100% rename from web/lib/admin/views/audit-logs/index.tsx rename to web/sdk/admin/views/audit-logs/index.tsx diff --git a/web/lib/admin/views/audit-logs/navbar.tsx b/web/sdk/admin/views/audit-logs/navbar.tsx similarity index 100% rename from web/lib/admin/views/audit-logs/navbar.tsx rename to web/sdk/admin/views/audit-logs/navbar.tsx diff --git a/web/lib/admin/views/audit-logs/sidepanel-details.tsx b/web/sdk/admin/views/audit-logs/sidepanel-details.tsx similarity index 100% rename from web/lib/admin/views/audit-logs/sidepanel-details.tsx rename to web/sdk/admin/views/audit-logs/sidepanel-details.tsx diff --git a/web/lib/admin/views/audit-logs/sidepanel-list-id.tsx b/web/sdk/admin/views/audit-logs/sidepanel-list-id.tsx similarity index 100% rename from web/lib/admin/views/audit-logs/sidepanel-list-id.tsx rename to web/sdk/admin/views/audit-logs/sidepanel-list-id.tsx diff --git a/web/lib/admin/views/audit-logs/sidepanel-list-link.tsx b/web/sdk/admin/views/audit-logs/sidepanel-list-link.tsx similarity index 100% rename from web/lib/admin/views/audit-logs/sidepanel-list-link.tsx rename to web/sdk/admin/views/audit-logs/sidepanel-list-link.tsx diff --git a/web/lib/admin/views/audit-logs/sidepanel-log-dialog.tsx b/web/sdk/admin/views/audit-logs/sidepanel-log-dialog.tsx similarity index 100% rename from web/lib/admin/views/audit-logs/sidepanel-log-dialog.tsx rename to web/sdk/admin/views/audit-logs/sidepanel-log-dialog.tsx diff --git a/web/lib/admin/views/audit-logs/util.ts b/web/sdk/admin/views/audit-logs/util.ts similarity index 100% rename from web/lib/admin/views/audit-logs/util.ts rename to web/sdk/admin/views/audit-logs/util.ts diff --git a/web/lib/admin/views/invoices/columns.tsx b/web/sdk/admin/views/invoices/columns.tsx similarity index 100% rename from web/lib/admin/views/invoices/columns.tsx rename to web/sdk/admin/views/invoices/columns.tsx diff --git a/web/lib/admin/views/invoices/index.tsx b/web/sdk/admin/views/invoices/index.tsx similarity index 100% rename from web/lib/admin/views/invoices/index.tsx rename to web/sdk/admin/views/invoices/index.tsx diff --git a/web/lib/admin/views/invoices/invoices.module.css b/web/sdk/admin/views/invoices/invoices.module.css similarity index 100% rename from web/lib/admin/views/invoices/invoices.module.css rename to web/sdk/admin/views/invoices/invoices.module.css diff --git a/web/lib/admin/views/invoices/navbar.tsx b/web/sdk/admin/views/invoices/navbar.tsx similarity index 100% rename from web/lib/admin/views/invoices/navbar.tsx rename to web/sdk/admin/views/invoices/navbar.tsx diff --git a/web/apps/admin/src/pages/organizations/details/apis/apis.module.css b/web/sdk/admin/views/organizations/details/apis/apis.module.css similarity index 100% rename from web/apps/admin/src/pages/organizations/details/apis/apis.module.css rename to web/sdk/admin/views/organizations/details/apis/apis.module.css diff --git a/web/apps/admin/src/pages/organizations/details/apis/columns.tsx b/web/sdk/admin/views/organizations/details/apis/columns.tsx similarity index 96% rename from web/apps/admin/src/pages/organizations/details/apis/columns.tsx rename to web/sdk/admin/views/organizations/details/apis/columns.tsx index 48431adc0..95b39e355 100644 --- a/web/apps/admin/src/pages/organizations/details/apis/columns.tsx +++ b/web/sdk/admin/views/organizations/details/apis/columns.tsx @@ -1,6 +1,6 @@ import { Text, type DataTableColumnDef } from "@raystack/apsara"; import dayjs from "dayjs"; -import { NULL_DATE } from "~/utils/constants"; +import { NULL_DATE } from "../../../../utils/constants"; import styles from "./apis.module.css"; import type { SearchOrganizationServiceUsersResponse_OrganizationServiceUser, diff --git a/web/apps/admin/src/pages/organizations/details/apis/details-dialog.tsx b/web/sdk/admin/views/organizations/details/apis/details-dialog.tsx similarity index 98% rename from web/apps/admin/src/pages/organizations/details/apis/details-dialog.tsx rename to web/sdk/admin/views/organizations/details/apis/details-dialog.tsx index d3179ef9d..a490d2731 100644 --- a/web/apps/admin/src/pages/organizations/details/apis/details-dialog.tsx +++ b/web/sdk/admin/views/organizations/details/apis/details-dialog.tsx @@ -10,7 +10,7 @@ import { type SearchOrganizationServiceUsersResponse_OrganizationServiceUser } from "@raystack/proton/frontier"; import { create } from "@bufbuild/protobuf"; -import { timestampToDayjs } from "~/utils/connect-timestamp"; +import { timestampToDayjs } from "../../../../utils/connect-timestamp"; interface ServiceUserDetailsDialogProps { onClose: () => void; diff --git a/web/apps/admin/src/pages/organizations/details/apis/index.tsx b/web/sdk/admin/views/organizations/details/apis/index.tsx similarity index 96% rename from web/apps/admin/src/pages/organizations/details/apis/index.tsx rename to web/sdk/admin/views/organizations/details/apis/index.tsx index c29cf93fe..7426efc89 100644 --- a/web/apps/admin/src/pages/organizations/details/apis/index.tsx +++ b/web/sdk/admin/views/organizations/details/apis/index.tsx @@ -4,7 +4,7 @@ import styles from "./apis.module.css"; import { InfoCircledIcon, ExclamationTriangleIcon } from "@radix-ui/react-icons"; import { useCallback, useContext, useEffect, useMemo, useState } from "react"; import { OrganizationContext } from "../contexts/organization-context"; -import PageTitle from "~/components/page-title"; +import { PageTitle } from "../../../../components/PageTitle"; import { getColumns } from "./columns"; import { ServiceUserDetailsDialog } from "./details-dialog"; import { useInfiniteQuery } from "@connectrpc/connect-query"; @@ -13,11 +13,11 @@ import { type SearchOrganizationServiceUsersResponse_OrganizationServiceUser, } from "@raystack/proton/frontier"; import { - transformDataTableQueryToRQLRequest, getConnectNextPageParam, getGroupCountMapFromFirstPage, DEFAULT_PAGE_SIZE, -} from "@raystack/frontier/admin"; +} from "../../../../utils/connect-pagination"; +import { transformDataTableQueryToRQLRequest } from "../../../../utils/transform-query"; import { useDebounceValue } from "usehooks-ts"; const NoCredentials = () => { diff --git a/web/apps/admin/src/pages/organizations/details/contexts/organization-context.tsx b/web/sdk/admin/views/organizations/details/contexts/organization-context.tsx similarity index 89% rename from web/apps/admin/src/pages/organizations/details/contexts/organization-context.tsx rename to web/sdk/admin/views/organizations/details/contexts/organization-context.tsx index 0a5e27242..d4f0854af 100644 --- a/web/apps/admin/src/pages/organizations/details/contexts/organization-context.tsx +++ b/web/sdk/admin/views/organizations/details/contexts/organization-context.tsx @@ -34,6 +34,10 @@ interface OrganizationContextType { updateKYCDetails: (kycDetails: OrganizationKyc | undefined) => void; kycDetails?: OrganizationKyc; isKYCLoading: boolean; + appUrl?: string; + tokenProductId?: string; + countries?: string[]; + organizationTypes?: string[]; } const defaultOrganiztionContextValue = { @@ -56,6 +60,10 @@ const defaultOrganiztionContextValue = { updateKYCDetails: () => {}, kycDetails: undefined, isKYCLoading: false, + appUrl: undefined, + tokenProductId: undefined, + countries: undefined, + organizationTypes: undefined, }; export const OrganizationContext = createContext(defaultOrganiztionContextValue); diff --git a/web/apps/admin/src/pages/organizations/details/edit/billing.tsx b/web/sdk/admin/views/organizations/details/edit/billing.tsx similarity index 100% rename from web/apps/admin/src/pages/organizations/details/edit/billing.tsx rename to web/sdk/admin/views/organizations/details/edit/billing.tsx diff --git a/web/apps/admin/src/pages/organizations/details/edit/edit.module.css b/web/sdk/admin/views/organizations/details/edit/edit.module.css similarity index 100% rename from web/apps/admin/src/pages/organizations/details/edit/edit.module.css rename to web/sdk/admin/views/organizations/details/edit/edit.module.css diff --git a/web/apps/admin/src/pages/organizations/details/edit/kyc.tsx b/web/sdk/admin/views/organizations/details/edit/kyc.tsx similarity index 100% rename from web/apps/admin/src/pages/organizations/details/edit/kyc.tsx rename to web/sdk/admin/views/organizations/details/edit/kyc.tsx diff --git a/web/apps/admin/src/pages/organizations/details/edit/organization.tsx b/web/sdk/admin/views/organizations/details/edit/organization.tsx similarity index 94% rename from web/apps/admin/src/pages/organizations/details/edit/organization.tsx rename to web/sdk/admin/views/organizations/details/edit/organization.tsx index f21964260..fbfedf193 100644 --- a/web/apps/admin/src/pages/organizations/details/edit/organization.tsx +++ b/web/sdk/admin/views/organizations/details/edit/organization.tsx @@ -13,8 +13,7 @@ import { Label, } from "@raystack/apsara"; import { Cross1Icon } from "@radix-ui/react-icons"; -import { AvatarUpload } from "@raystack/frontier/react"; -import { AppContext } from "~/contexts/App"; +import { AvatarUpload } from "../../../../../react/components/avatar-upload"; import { z } from "zod"; import { Controller, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -44,11 +43,6 @@ const orgUpdateSchema = z type OrgUpdateSchema = z.infer; -async function loadCountries() { - const data = await import("~/assets/data/countries.json"); - return data.default; -} - interface MetaData extends Record { size?: number; type?: string; @@ -83,20 +77,15 @@ function getDefaultValue(organization: Organization, industries: string[]) { } export function EditOrganizationPanel({ onClose }: { onClose: () => void }) { - const { config } = useContext(AppContext); - const { organization } = useContext(OrganizationContext); - const [countries, setCountries] = useState([]); + const { organization, appUrl, countries: countriesFromContext = [], organizationTypes: industries = [] } = useContext(OrganizationContext); + const [countries, setCountries] = useState(countriesFromContext); const queryClient = useQueryClient(); const transport = useTransport(); - - const industries = config?.organization_types || []; const orgId = organization?.id || ""; useEffect(() => { - loadCountries().then((list) => - setCountries(list.map((country) => country.name)), - ); - }, []); + if (countriesFromContext.length > 0) setCountries(countriesFromContext); + }, [countriesFromContext]); const { handleSubmit, @@ -216,7 +205,7 @@ export function EditOrganizationPanel({ onClose }: { onClose: () => void }) { Promise; + onExportProjects?: () => Promise; + onExportTokens?: () => Promise; + appUrl?: string; + tokenProductId?: string; + countries?: string[]; + organizationTypes?: string[]; +}; + +export const OrganizationDetails = ({ + organizationId, + onExportMembers, + onExportProjects, + onExportTokens, + appUrl, + tokenProductId, + countries, + organizationTypes, +}: OrganizationDetailsProps) => { + const [isSearchVisible, setIsSearchVisible] = useState(false); + const [searchQuery, setSearchQuery] = useState(""); + const queryClient = useQueryClient(); + const transport = useTransport(); + + // Use Connect RPC for fetching organization + const { + data: organization, + isLoading: isOrganizationLoading, + error: organizationError, + } = useQuery( + FrontierServiceQueries.getOrganization, + { id: organizationId }, + { + enabled: !!organizationId, + select: (data) => data?.organization, + }, + ); + + const getOrganizationQueryKey = [ + FrontierServiceQueries.getOrganization, + { id: organizationId }, + ]; + + async function updateOrganization(org: Organization) { + queryClient.setQueryData(getOrganizationQueryKey, { organization: org }); + } + + // Fetch KYC details + const { + data: kycData, + isLoading: isKYCLoading, + error: kycError, + } = useQuery( + FrontierServiceQueries.getOrganizationKyc, + { orgId: organizationId || "" }, + { + enabled: !!organizationId, + }, + ); + + const kycDetails = useMemo(() => kycData?.organizationKyc, [kycData]); + + function updateKYCDetails(kyc: typeof kycDetails) { + if (!organizationId) return; + queryClient.setQueryData( + createConnectQueryKey({ + schema: FrontierServiceQueries.getOrganizationKyc, + transport, + input: { orgId: organizationId }, + cardinality: "finite", + }), + create(GetOrganizationKycResponseSchema, { organizationKyc: kyc }), + ); + } + + // Fetch default roles + const { + data: defaultRoles = [], + isLoading: isDefaultRolesLoading, + error: defaultRolesError, + } = useQuery( + FrontierServiceQueries.listRoles, + { scopes: [ORG_NAMESPACE] }, + { + enabled: !!organizationId, + select: (data) => data?.roles || [], + }, + ); + + // Fetch organization-specific roles + const { + data: organizationRoles = [], + isLoading: isOrgRolesLoading, + error: orgRolesError, + } = useQuery( + FrontierServiceQueries.listOrganizationRoles, + { orgId: organizationId || "", scopes: [ORG_NAMESPACE] }, + { + enabled: !!organizationId, + select: (data) => data?.roles || [], + }, + ); + + const roles = [...defaultRoles, ...organizationRoles]; + + // Fetch organization members + const { + data: orgMembersMap = {}, + isLoading: isOrgMembersMapLoading, + error: orgMembersError, + } = useQuery( + FrontierServiceQueries.listOrganizationUsers, + { id: organizationId || "" }, + { + enabled: !!organizationId, + select: (data) => { + const users = data?.users || []; + return users.reduce( + (acc, user) => { + const id = user.id || ""; + acc[id] = user; + return acc; + }, + {} as Record, + ); + }, + }, + ); + + // Fetch billing accounts list + const { data: firstBillingAccountId = "", error: billingAccountsError } = + useQuery( + FrontierServiceQueries.listBillingAccounts, + { orgId: organizationId || "" }, + { + enabled: !!organizationId, + select: (data) => data?.billingAccounts?.[0]?.id || "", + }, + ); + + // Fetch billing account details + const { + data: billingAccountData, + isLoading: isBillingAccountLoading, + error: billingAccountError, + refetch: fetchBillingAccountDetails, + } = useQuery( + FrontierServiceQueries.getBillingAccount, + { + orgId: organizationId || "", + id: firstBillingAccountId, + withBillingDetails: true, + }, + { + enabled: !!organizationId && !!firstBillingAccountId, + select: (data) => ({ + billingAccount: data?.billingAccount, + billingAccountDetails: data?.billingDetails, + }), + }, + ); + + const billingAccount = billingAccountData?.billingAccount; + const billingAccountDetails = billingAccountData?.billingAccountDetails; + + // Fetch billing balance + const { + data: tokenBalance = "0", + isLoading: isTokenBalanceLoading, + error: tokenBalanceError, + refetch: fetchTokenBalance, + } = useQuery( + FrontierServiceQueries.getBillingBalance, + { + orgId: organizationId || "", + id: firstBillingAccountId, + }, + { + enabled: !!organizationId && !!firstBillingAccountId, + select: (data) => String(data?.balance?.amount || "0"), + }, + ); + + // Error handling + useEffect(() => { + if (organizationError) { + console.error("Failed to fetch organization:", organizationError); + } + if (kycError) { + console.error("Failed to fetch KYC details:", kycError); + } + if (defaultRolesError) { + console.error("Failed to fetch default roles:", defaultRolesError); + } + if (orgRolesError) { + console.error("Failed to fetch organization roles:", orgRolesError); + } + if (orgMembersError) { + console.error("Failed to fetch organization members:", orgMembersError); + } + if (billingAccountsError) { + console.error("Failed to fetch billing accounts:", billingAccountsError); + } + if (billingAccountError) { + console.error( + "Failed to fetch billing account details:", + billingAccountError, + ); + } + if (tokenBalanceError) { + console.error("Failed to fetch token balance:", tokenBalanceError); + } + }, [ + organizationError, + kycError, + defaultRolesError, + orgRolesError, + orgMembersError, + billingAccountsError, + billingAccountError, + tokenBalanceError, + ]); + + const isLoading = + isOrganizationLoading || + isDefaultRolesLoading || + isOrgRolesLoading || + isBillingAccountLoading; + return ( + + + {organization?.id ? ( + + ) : null} + + + ); +}; diff --git a/web/apps/admin/src/pages/organizations/details/invoices/columns.tsx b/web/sdk/admin/views/organizations/details/invoices/columns.tsx similarity index 95% rename from web/apps/admin/src/pages/organizations/details/invoices/columns.tsx rename to web/sdk/admin/views/organizations/details/invoices/columns.tsx index 36453ffbf..10f4e15ac 100644 --- a/web/apps/admin/src/pages/organizations/details/invoices/columns.tsx +++ b/web/sdk/admin/views/organizations/details/invoices/columns.tsx @@ -1,4 +1,4 @@ -import { NULL_DATE } from "~/utils/constants"; +import { NULL_DATE } from "../../../../utils/constants"; import styles from "./invoices.module.css"; import dayjs from "dayjs"; import { DataTableColumnDef, Link, Amount } from "@raystack/apsara"; @@ -9,7 +9,7 @@ import { isNullTimestamp, TimeStamp, timestampToDate, -} from "~/utils/connect-timestamp"; +} from "../../../../utils/connect-timestamp"; // https://docs.stripe.com/invoicing/overview#invoice-statuses const InvoiceStatusesMap = { diff --git a/web/apps/admin/src/pages/organizations/details/invoices/index.tsx b/web/sdk/admin/views/organizations/details/invoices/index.tsx similarity index 96% rename from web/apps/admin/src/pages/organizations/details/invoices/index.tsx rename to web/sdk/admin/views/organizations/details/invoices/index.tsx index a251d251a..bbe73dba7 100644 --- a/web/apps/admin/src/pages/organizations/details/invoices/index.tsx +++ b/web/sdk/admin/views/organizations/details/invoices/index.tsx @@ -4,16 +4,16 @@ import styles from "./invoices.module.css"; import { FileTextIcon, ExclamationTriangleIcon } from "@radix-ui/react-icons"; import { useContext, useEffect, useMemo, useState } from "react"; import { OrganizationContext } from "../contexts/organization-context"; -import PageTitle from "~/components/page-title"; +import { PageTitle } from "../../../../components/PageTitle"; import { getColumns } from "./columns"; import { AdminServiceQueries } from "@raystack/proton/frontier"; import { useInfiniteQuery } from "@connectrpc/connect-query"; import { - transformDataTableQueryToRQLRequest, getConnectNextPageParam, DEFAULT_PAGE_SIZE, getGroupCountMapFromFirstPage, -} from "@raystack/frontier/admin"; +} from "../../../../utils/connect-pagination"; +import { transformDataTableQueryToRQLRequest } from "../../../../utils/transform-query"; import { useDebounceValue } from "usehooks-ts"; const DEFAULT_SORT: DataTableSort = { name: "created_at", order: "desc" }; diff --git a/web/apps/admin/src/pages/organizations/details/invoices/invoices.module.css b/web/sdk/admin/views/organizations/details/invoices/invoices.module.css similarity index 100% rename from web/apps/admin/src/pages/organizations/details/invoices/invoices.module.css rename to web/sdk/admin/views/organizations/details/invoices/invoices.module.css diff --git a/web/apps/admin/src/pages/organizations/details/layout/add-tokens-dialog.tsx b/web/sdk/admin/views/organizations/details/layout/add-tokens-dialog.tsx similarity index 94% rename from web/apps/admin/src/pages/organizations/details/layout/add-tokens-dialog.tsx rename to web/sdk/admin/views/organizations/details/layout/add-tokens-dialog.tsx index 85ae6787b..9ff9c1e34 100644 --- a/web/apps/admin/src/pages/organizations/details/layout/add-tokens-dialog.tsx +++ b/web/sdk/admin/views/organizations/details/layout/add-tokens-dialog.tsx @@ -13,8 +13,7 @@ import { Controller, FormProvider, useForm } from "react-hook-form"; import styles from "./layout.module.css"; import * as z from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; -import { AppContext } from "~/contexts/App"; -import { defaultConfig } from "~/utils/constants"; +import { defaultConfig } from "../../../../utils/constants"; import { useMutation, createConnectQueryKey, useTransport } from "@connectrpc/connect-query"; import { useQueryClient } from "@tanstack/react-query"; import { AdminServiceQueries, CheckoutProductBodySchema, DelegatedCheckoutRequestSchema } from "@raystack/proton/frontier"; @@ -38,8 +37,7 @@ const addTokensSchema = z.object({ type AddTokenRequestType = z.infer; export const AddTokensDialog = ({ onOpenChange }: InviteUsersDialogProps) => { - const { config } = useContext(AppContext); - const { organization, billingAccount, fetchTokenBalance } = + const { organization, billingAccount, fetchTokenBalance, tokenProductId } = useContext(OrganizationContext); const queryClient = useQueryClient(); const transport = useTransport(); @@ -50,7 +48,7 @@ export const AddTokensDialog = ({ onOpenChange }: InviteUsersDialogProps) => { resolver: zodResolver(addTokensSchema), defaultValues: { quantity: "0", - product: config?.token_product_id || defaultConfig.token_product_id, + product: tokenProductId ?? defaultConfig.token_product_id ?? "token", }, }); diff --git a/web/apps/admin/src/pages/organizations/details/layout/index.tsx b/web/sdk/admin/views/organizations/details/layout/index.tsx similarity index 84% rename from web/apps/admin/src/pages/organizations/details/layout/index.tsx rename to web/sdk/admin/views/organizations/details/layout/index.tsx index 73e5b0e68..fc7482be7 100644 --- a/web/apps/admin/src/pages/organizations/details/layout/index.tsx +++ b/web/sdk/admin/views/organizations/details/layout/index.tsx @@ -1,11 +1,10 @@ import { OrganizationsDetailsNavabar } from "./navbar"; import styles from "./layout.module.css"; -import { EmptyState, Flex } from "@raystack/apsara"; +import { EmptyState, Flex, Spinner } from "@raystack/apsara"; import { OrgSidePanel } from "../side-panel/"; import React, { useState } from "react"; -import LoadingState from "~/components/states/Loading"; import { OrganizationIcon } from "@raystack/apsara/icons"; -import PageTitle from "~/components/page-title"; +import { PageTitle } from "../../../../components/PageTitle"; import { EditKYCPanel } from "../edit/kyc"; import { EditOrganizationPanel } from "../edit/organization"; import { EditBillingPanel } from "../edit/billing"; @@ -15,12 +14,18 @@ interface OrganizationDetailsLayoutProps { isLoading: boolean; organization?: Organization; children: React.ReactNode; + onExportMembers?: () => Promise; + onExportProjects?: () => Promise; + onExportTokens?: () => Promise; } export const OrganizationDetailsLayout = ({ isLoading, organization, children, + onExportMembers, + onExportProjects, + onExportTokens, }: OrganizationDetailsLayoutProps) => { const [showSidePanel, setShowSidePanel] = useState(true); const [showKYCPanel, setShowKYCPanel] = useState(false); @@ -58,8 +63,9 @@ export const OrganizationDetailsLayout = ({ const title = `${organization?.title} | Organizations`; return isLoading ? ( - // TODO: make better loading state for page - + + + ) : organization ? ( @@ -69,6 +75,9 @@ export const OrganizationDetailsLayout = ({ openKYCPanel={openKYCPanel} openEditOrgPanel={openEditOrgPanel} openEditBillingPanel={openEditBillingPanel} + onExportMembers={onExportMembers} + onExportProjects={onExportProjects} + onExportTokens={onExportTokens} /> void; openEditOrgPanel: () => void; openEditBillingPanel: () => void; + onExportMembers?: () => Promise; + onExportProjects?: () => Promise; + onExportTokens?: () => Promise; } const NavbarActionMenu = ({ @@ -70,6 +63,9 @@ const NavbarActionMenu = ({ openEditOrgPanel, openEditBillingPanel, organizationTitle, + onExportMembers, + onExportProjects, + onExportTokens, }: NavbarActionMenuProps) => { const [isInviteUsersDialogOpen, setIsInviteUsersDialogOpen] = useState(false); const [isAddTokensDialogOpen, setIsAddTokensDialogOpen] = useState(false); @@ -87,13 +83,10 @@ const NavbarActionMenu = ({ async function handleExportMembers(e: React.MouseEvent) { e.preventDefault(); + if (!onExportMembers) return; try { setIsMembersDownloading(true); - await exportCsvFromStream( - adminClient.exportOrganizationUsers, - create(ExportOrganizationUsersRequestSchema, { id: organizationId }), - `${organizationTitle}_members.csv`, - ); + await onExportMembers(); } catch (error) { toast.error("Failed to export members"); console.error(error); @@ -104,13 +97,10 @@ const NavbarActionMenu = ({ async function handleExportProjects(e: React.MouseEvent) { e.preventDefault(); + if (!onExportProjects) return; try { setIsProjectsDownloading(true); - await exportCsvFromStream( - adminClient.exportOrganizationProjects, - create(ExportOrganizationProjectsRequestSchema, { id: organizationId }), - `${organizationTitle}_projects.csv`, - ); + await onExportProjects(); } catch (error) { toast.error("Failed to export projects"); console.error(error); @@ -121,13 +111,10 @@ const NavbarActionMenu = ({ async function handleExportTokens(e: React.MouseEvent) { e.preventDefault(); + if (!onExportTokens) return; try { setIsTokensDownloading(true); - await exportCsvFromStream( - adminClient.exportOrganizationTokens, - create(ExportOrganizationTokensRequestSchema, { id: organizationId }), - `${organizationTitle}_tokens.csv`, - ); + await onExportTokens(); } catch (error) { toast.error("Failed to export tokens"); console.error(error); @@ -136,22 +123,36 @@ const NavbarActionMenu = ({ } } + const exportSubItems: navConfig[] = []; + if (onExportMembers) { + exportSubItems.push({ + label: "Members", + onClick: handleExportMembers, + isLoading: isMembersDownloading, + }); + } + if (onExportProjects) { + exportSubItems.push({ + label: "Projects", + onClick: handleExportProjects, + isLoading: isProjectsDownloading, + }); + } + if (onExportTokens) { + exportSubItems.push({ + label: "Tokens", + onClick: handleExportTokens, + isLoading: isTokensDownloading, + }); + } + const items: navConfig[] = [ { label: "Edit...", subItems: [ - { - label: "Organization...", - onClick: openEditOrgPanel, - }, - { - label: "Billing...", - onClick: openEditBillingPanel, - }, - { - label: "KYC...", - onClick: openKYCPanel, - }, + { label: "Organization...", onClick: openEditOrgPanel }, + { label: "Billing...", onClick: openEditBillingPanel }, + { label: "KYC...", onClick: openKYCPanel }, ], }, { @@ -170,26 +171,9 @@ const NavbarActionMenu = ({ label: "Change plan...", disabled: true, }, - { - label: "Export", - subItems: [ - { - label: "Members", - onClick: handleExportMembers, - isLoading: isMembersDownloading, - }, - { - label: "Projects", - onClick: handleExportProjects, - isLoading: isProjectsDownloading, - }, - { - label: "Tokens", - onClick: handleExportTokens, - isLoading: isTokensDownloading, - }, - ], - }, + ...(exportSubItems.length > 0 + ? [{ label: "Export", subItems: exportSubItems }] + : []), ]; return ( @@ -278,6 +262,9 @@ interface OrganizationDetailsNavbarProps { openKYCPanel: () => void; openEditOrgPanel: () => void; openEditBillingPanel: () => void; + onExportMembers?: () => Promise; + onExportProjects?: () => Promise; + onExportTokens?: () => Promise; } export const OrganizationsDetailsNavabar = ({ @@ -286,6 +273,9 @@ export const OrganizationsDetailsNavabar = ({ openKYCPanel, openEditBillingPanel, openEditOrgPanel, + onExportMembers, + onExportProjects, + onExportTokens, }: OrganizationDetailsNavbarProps) => { const { search } = useContext(OrganizationContext); @@ -325,14 +315,18 @@ export const OrganizationsDetailsNavabar = ({ openEditOrgPanel={openEditOrgPanel} openEditBillingPanel={openEditBillingPanel} organizationTitle={organization?.title || ""} + onExportMembers={onExportMembers} + onExportProjects={onExportProjects} + onExportTokens={onExportTokens} /> {search.isVisible ? ( - ) : null} diff --git a/web/apps/admin/src/pages/organizations/details/members/columns.tsx b/web/sdk/admin/views/organizations/details/members/columns.tsx similarity index 98% rename from web/apps/admin/src/pages/organizations/details/members/columns.tsx rename to web/sdk/admin/views/organizations/details/members/columns.tsx index 66ec0c6b1..a102f1926 100644 --- a/web/apps/admin/src/pages/organizations/details/members/columns.tsx +++ b/web/sdk/admin/views/organizations/details/members/columns.tsx @@ -17,7 +17,7 @@ import { isNullTimestamp, TimeStamp, timestampToDate, -} from "~/utils/connect-timestamp"; +} from "../../../../utils/connect-timestamp"; const MemberStates = { enabled: "Active", diff --git a/web/apps/admin/src/pages/organizations/details/members/index.tsx b/web/sdk/admin/views/organizations/details/members/index.tsx similarity index 94% rename from web/apps/admin/src/pages/organizations/details/members/index.tsx rename to web/sdk/admin/views/organizations/details/members/index.tsx index c9c6f677f..f3681cbf7 100644 --- a/web/apps/admin/src/pages/organizations/details/members/index.tsx +++ b/web/sdk/admin/views/organizations/details/members/index.tsx @@ -1,6 +1,6 @@ import { DataTable, EmptyState, Flex } from "@raystack/apsara"; import type { DataTableQuery, DataTableSort } from "@raystack/apsara"; -import PageTitle from "~/components/page-title"; +import { PageTitle } from "../../../../components/PageTitle"; import styles from "./members.module.css"; import { useContext, useEffect, useMemo, useState } from "react"; import { getColumns } from "./columns"; @@ -12,16 +12,13 @@ import { useTransport, } from "@connectrpc/connect-query"; import { useQueryClient } from "@tanstack/react-query"; -import UserIcon from "~/assets/icons/users.svg?react"; +import { UsersIcon } from "../../../../assets/icons/UsersIcon"; import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; import { OrganizationContext } from "../contexts/organization-context"; -import { AssignRole } from "~/components/assign-role"; +import { AssignRole } from "../../../../components/AssignRole"; import { RemoveMember } from "./remove-member"; -import { - transformDataTableQueryToRQLRequest, - getConnectNextPageParam, - DEFAULT_PAGE_SIZE, -} from "@raystack/frontier/admin"; +import { getConnectNextPageParam, DEFAULT_PAGE_SIZE } from "../../../../utils/connect-pagination"; +import { transformDataTableQueryToRQLRequest } from "../../../../utils/transform-query"; import { useDebounceValue } from "usehooks-ts"; const DEFAULT_SORT: DataTableSort = { name: "org_joined_at", order: "desc" }; @@ -47,7 +44,7 @@ const NoMembers = () => { }} heading="No Member found" subHeading="We couldn't find any matches for that keyword or filter. Try alternative terms or check for typos." - icon={} + icon={} /> ); }; diff --git a/web/apps/admin/src/pages/organizations/details/members/members.module.css b/web/sdk/admin/views/organizations/details/members/members.module.css similarity index 100% rename from web/apps/admin/src/pages/organizations/details/members/members.module.css rename to web/sdk/admin/views/organizations/details/members/members.module.css diff --git a/web/apps/admin/src/pages/organizations/details/members/remove-member.tsx b/web/sdk/admin/views/organizations/details/members/remove-member.tsx similarity index 100% rename from web/apps/admin/src/pages/organizations/details/members/remove-member.tsx rename to web/sdk/admin/views/organizations/details/members/remove-member.tsx diff --git a/web/apps/admin/src/pages/organizations/details/projects/columns.tsx b/web/sdk/admin/views/organizations/details/projects/columns.tsx similarity index 99% rename from web/apps/admin/src/pages/organizations/details/projects/columns.tsx rename to web/sdk/admin/views/organizations/details/projects/columns.tsx index 1c3cbd95c..c8460c647 100644 --- a/web/apps/admin/src/pages/organizations/details/projects/columns.tsx +++ b/web/sdk/admin/views/organizations/details/projects/columns.tsx @@ -18,7 +18,7 @@ import { isNullTimestamp, TimeStamp, timestampToDate, -} from "~/utils/connect-timestamp"; +} from "../../../../utils/connect-timestamp"; import { DotsHorizontalIcon } from "@radix-ui/react-icons"; import { RenameProjectDialog } from "./rename-project"; import { useState } from "react"; diff --git a/web/apps/admin/src/pages/organizations/details/projects/index.tsx b/web/sdk/admin/views/organizations/details/projects/index.tsx similarity index 95% rename from web/apps/admin/src/pages/organizations/details/projects/index.tsx rename to web/sdk/admin/views/organizations/details/projects/index.tsx index ed23f0744..5220164f8 100644 --- a/web/apps/admin/src/pages/organizations/details/projects/index.tsx +++ b/web/sdk/admin/views/organizations/details/projects/index.tsx @@ -5,7 +5,7 @@ import { type DataTableQuery, type DataTableSort, } from "@raystack/apsara"; -import PageTitle from "~/components/page-title"; +import { PageTitle } from "../../../../components/PageTitle"; import styles from "./projects.module.css"; import { useContext, useEffect, useMemo, useState } from "react"; import { getColumns } from "./columns"; @@ -20,11 +20,8 @@ import { useQueryClient } from "@tanstack/react-query"; import { OrganizationContext } from "../contexts/organization-context"; import { FileIcon, ExclamationTriangleIcon } from "@radix-ui/react-icons"; import { ProjectMembersDialog } from "./members"; -import { - transformDataTableQueryToRQLRequest, - getConnectNextPageParam, - DEFAULT_PAGE_SIZE, -} from "@raystack/frontier/admin"; +import { getConnectNextPageParam, DEFAULT_PAGE_SIZE } from "../../../../utils/connect-pagination"; +import { transformDataTableQueryToRQLRequest } from "../../../../utils/transform-query"; import { useDebounceValue } from "usehooks-ts"; const DEFAULT_SORT: DataTableSort = { name: "created_at", order: "desc" }; diff --git a/web/apps/admin/src/pages/organizations/details/projects/members/add-members-dropdown.tsx b/web/sdk/admin/views/organizations/details/projects/members/add-members-dropdown.tsx similarity index 100% rename from web/apps/admin/src/pages/organizations/details/projects/members/add-members-dropdown.tsx rename to web/sdk/admin/views/organizations/details/projects/members/add-members-dropdown.tsx diff --git a/web/apps/admin/src/pages/organizations/details/projects/members/assign-role.tsx b/web/sdk/admin/views/organizations/details/projects/members/assign-role.tsx similarity index 100% rename from web/apps/admin/src/pages/organizations/details/projects/members/assign-role.tsx rename to web/sdk/admin/views/organizations/details/projects/members/assign-role.tsx diff --git a/web/apps/admin/src/pages/organizations/details/projects/members/columns.tsx b/web/sdk/admin/views/organizations/details/projects/members/columns.tsx similarity index 100% rename from web/apps/admin/src/pages/organizations/details/projects/members/columns.tsx rename to web/sdk/admin/views/organizations/details/projects/members/columns.tsx diff --git a/web/apps/admin/src/pages/organizations/details/projects/members/index.tsx b/web/sdk/admin/views/organizations/details/projects/members/index.tsx similarity index 96% rename from web/apps/admin/src/pages/organizations/details/projects/members/index.tsx rename to web/sdk/admin/views/organizations/details/projects/members/index.tsx index 6b8d95f5a..c2ed1f009 100644 --- a/web/apps/admin/src/pages/organizations/details/projects/members/index.tsx +++ b/web/sdk/admin/views/organizations/details/projects/members/index.tsx @@ -14,17 +14,14 @@ import { create } from "@bufbuild/protobuf"; import { useQuery, useInfiniteQuery } from "@connectrpc/connect-query"; import { useDebouncedState } from "@raystack/apsara/hooks"; import styles from "./members.module.css"; -import UserIcon from "~/assets/icons/users.svg?react"; +import { UsersIcon } from "../../../../../assets/icons/UsersIcon"; import { getColumns } from "./columns"; import { AssignRole } from "./assign-role"; import { PROJECT_NAMESPACE } from "../../types"; import { RemoveMember } from "./remove-member"; import { AddMembersDropdown } from "./add-members-dropdown"; -import { - getConnectNextPageParam, - DEFAULT_PAGE_SIZE, - transformDataTableQueryToRQLRequest, -} from "@raystack/frontier/admin"; +import { getConnectNextPageParam, DEFAULT_PAGE_SIZE } from "../../../../../utils/connect-pagination"; +import { transformDataTableQueryToRQLRequest } from "../../../../../utils/transform-query"; const NoMembers = () => { return ( @@ -35,7 +32,7 @@ const NoMembers = () => { }} heading="No Members found" subHeading="We couldn’t find any matches for that keyword or filter. Try alternative terms or check for typos." - icon={} + icon={} /> ); }; diff --git a/web/apps/admin/src/pages/organizations/details/projects/members/members.module.css b/web/sdk/admin/views/organizations/details/projects/members/members.module.css similarity index 100% rename from web/apps/admin/src/pages/organizations/details/projects/members/members.module.css rename to web/sdk/admin/views/organizations/details/projects/members/members.module.css diff --git a/web/apps/admin/src/pages/organizations/details/projects/members/remove-member.tsx b/web/sdk/admin/views/organizations/details/projects/members/remove-member.tsx similarity index 100% rename from web/apps/admin/src/pages/organizations/details/projects/members/remove-member.tsx rename to web/sdk/admin/views/organizations/details/projects/members/remove-member.tsx diff --git a/web/apps/admin/src/pages/organizations/details/projects/projects.module.css b/web/sdk/admin/views/organizations/details/projects/projects.module.css similarity index 100% rename from web/apps/admin/src/pages/organizations/details/projects/projects.module.css rename to web/sdk/admin/views/organizations/details/projects/projects.module.css diff --git a/web/apps/admin/src/pages/organizations/details/projects/rename-project.tsx b/web/sdk/admin/views/organizations/details/projects/rename-project.tsx similarity index 100% rename from web/apps/admin/src/pages/organizations/details/projects/rename-project.tsx rename to web/sdk/admin/views/organizations/details/projects/rename-project.tsx diff --git a/web/apps/admin/src/pages/organizations/details/projects/use-add-project-members.tsx b/web/sdk/admin/views/organizations/details/projects/use-add-project-members.tsx similarity index 97% rename from web/apps/admin/src/pages/organizations/details/projects/use-add-project-members.tsx rename to web/sdk/admin/views/organizations/details/projects/use-add-project-members.tsx index eb3dee62d..5df76a670 100644 --- a/web/apps/admin/src/pages/organizations/details/projects/use-add-project-members.tsx +++ b/web/sdk/admin/views/organizations/details/projects/use-add-project-members.tsx @@ -1,7 +1,7 @@ import { useCallback, useContext, useMemo, useState } from "react"; import { OrganizationContext } from "../contexts/organization-context"; import { toast } from "@raystack/apsara"; -import { DEFAULT_ROLES } from "~/utils/constants"; +import { DEFAULT_ROLES } from "../../../../utils/constants"; import { useQuery, useMutation } from "@connectrpc/connect-query"; import { FrontierServiceQueries, ListProjectUsersRequestSchema, CreatePolicyRequestSchema } from "@raystack/proton/frontier"; import { create } from "@bufbuild/protobuf"; diff --git a/web/apps/admin/src/pages/organizations/details/security/block-organization.tsx b/web/sdk/admin/views/organizations/details/security/block-organization.tsx similarity index 100% rename from web/apps/admin/src/pages/organizations/details/security/block-organization.tsx rename to web/sdk/admin/views/organizations/details/security/block-organization.tsx diff --git a/web/apps/admin/src/pages/organizations/details/security/domains-list.tsx b/web/sdk/admin/views/organizations/details/security/domains-list.tsx similarity index 100% rename from web/apps/admin/src/pages/organizations/details/security/domains-list.tsx rename to web/sdk/admin/views/organizations/details/security/domains-list.tsx diff --git a/web/apps/admin/src/pages/organizations/details/security/index.tsx b/web/sdk/admin/views/organizations/details/security/index.tsx similarity index 97% rename from web/apps/admin/src/pages/organizations/details/security/index.tsx rename to web/sdk/admin/views/organizations/details/security/index.tsx index b333e9a4c..f9ca9dd48 100644 --- a/web/apps/admin/src/pages/organizations/details/security/index.tsx +++ b/web/sdk/admin/views/organizations/details/security/index.tsx @@ -14,7 +14,7 @@ import { useOutletContext } from "react-router-dom"; import { OutletContext } from "../types"; import { BlockOrganizationSection } from "./block-organization"; import { DomainsList } from "./domains-list"; -import PageTitle from "~/components/page-title"; +import { PageTitle } from "../../../../components/PageTitle"; import { useQuery } from "@connectrpc/connect-query"; import { FrontierServiceQueries, ListOrganizationDomainsRequestSchema } from "@raystack/proton/frontier"; import { create } from "@bufbuild/protobuf"; diff --git a/web/apps/admin/src/pages/organizations/details/security/security.module.css b/web/sdk/admin/views/organizations/details/security/security.module.css similarity index 100% rename from web/apps/admin/src/pages/organizations/details/security/security.module.css rename to web/sdk/admin/views/organizations/details/security/security.module.css diff --git a/web/apps/admin/src/pages/organizations/details/side-panel/billing-details-section.tsx b/web/sdk/admin/views/organizations/details/side-panel/billing-details-section.tsx similarity index 97% rename from web/apps/admin/src/pages/organizations/details/side-panel/billing-details-section.tsx rename to web/sdk/admin/views/organizations/details/side-panel/billing-details-section.tsx index cb3b488c6..424b250eb 100644 --- a/web/apps/admin/src/pages/organizations/details/side-panel/billing-details-section.tsx +++ b/web/sdk/admin/views/organizations/details/side-panel/billing-details-section.tsx @@ -1,6 +1,6 @@ import { CopyButton, Flex, Link, List, Text } from "@raystack/apsara"; import styles from "./side-panel.module.css"; -import { converBillingAddressToString } from "~/utils/helper"; +import { converBillingAddressToString } from "../../../../utils/helper"; import Skeleton from "react-loading-skeleton"; import { useContext, useEffect } from "react"; import { CalendarIcon } from "@radix-ui/react-icons"; @@ -9,7 +9,7 @@ import { OrganizationContext } from "../contexts/organization-context"; import { useQuery } from "@connectrpc/connect-query"; import { FrontierServiceQueries, GetUpcomingInvoiceRequestSchema } from "@raystack/proton/frontier"; import { create } from "@bufbuild/protobuf"; -import { timestampToDayjs } from "~/utils/connect-timestamp"; +import { timestampToDayjs } from "../../../../utils/connect-timestamp"; export const BillingDetailsSection = () => { const { billingAccount, organization } = useContext(OrganizationContext); diff --git a/web/apps/admin/src/pages/organizations/details/side-panel/index.tsx b/web/sdk/admin/views/organizations/details/side-panel/index.tsx similarity index 100% rename from web/apps/admin/src/pages/organizations/details/side-panel/index.tsx rename to web/sdk/admin/views/organizations/details/side-panel/index.tsx diff --git a/web/apps/admin/src/pages/organizations/details/side-panel/kyc-section.tsx b/web/sdk/admin/views/organizations/details/side-panel/kyc-section.tsx similarity index 100% rename from web/apps/admin/src/pages/organizations/details/side-panel/kyc-section.tsx rename to web/sdk/admin/views/organizations/details/side-panel/kyc-section.tsx diff --git a/web/apps/admin/src/pages/organizations/details/side-panel/org-details-section.tsx b/web/sdk/admin/views/organizations/details/side-panel/org-details-section.tsx similarity index 92% rename from web/apps/admin/src/pages/organizations/details/side-panel/org-details-section.tsx rename to web/sdk/admin/views/organizations/details/side-panel/org-details-section.tsx index a7571f60d..d100edee6 100644 --- a/web/apps/admin/src/pages/organizations/details/side-panel/org-details-section.tsx +++ b/web/sdk/admin/views/organizations/details/side-panel/org-details-section.tsx @@ -3,9 +3,9 @@ import { Flex, List, Text, CopyButton, Tooltip } from "@raystack/apsara"; import styles from "./side-panel.module.css"; import dayjs from "dayjs"; import { useContext } from "react"; -import { AppContext } from "~/contexts/App"; +import { OrganizationContext } from "../contexts/organization-context"; import type { Organization } from "@raystack/proton/frontier"; -import { timestampToDate } from "~/utils/connect-timestamp"; +import { timestampToDate } from "../../../../utils/connect-timestamp"; interface OrganizationDetailsSectionProps { organization: Organization; @@ -16,7 +16,7 @@ type Metadata = Record; export const OrganizationDetailsSection = ({ organization, }: OrganizationDetailsSectionProps) => { - const { config } = useContext(AppContext); + const { appUrl } = useContext(OrganizationContext); return ( @@ -27,7 +27,7 @@ export const OrganizationDetailsSection = ({ - {config?.app_url}/{organization.name} + {appUrl ? `${appUrl}/${organization.name}` : organization.name} diff --git a/web/apps/admin/src/pages/organizations/details/side-panel/plan-details-section.tsx b/web/sdk/admin/views/organizations/details/side-panel/plan-details-section.tsx similarity index 97% rename from web/apps/admin/src/pages/organizations/details/side-panel/plan-details-section.tsx rename to web/sdk/admin/views/organizations/details/side-panel/plan-details-section.tsx index 9afb543d8..7a570f75d 100644 --- a/web/apps/admin/src/pages/organizations/details/side-panel/plan-details-section.tsx +++ b/web/sdk/admin/views/organizations/details/side-panel/plan-details-section.tsx @@ -7,7 +7,7 @@ import { OrganizationContext } from "../contexts/organization-context"; import { useQuery } from "@connectrpc/connect-query"; import { FrontierServiceQueries, ListSubscriptionsRequestSchema, GetPlanRequestSchema } from "@raystack/proton/frontier"; import { create } from "@bufbuild/protobuf"; -import { timestampToDayjs } from "~/utils/connect-timestamp"; +import { timestampToDayjs } from "../../../../utils/connect-timestamp"; export const PlanDetailsSection = () => { const { billingAccount, organization } = useContext(OrganizationContext); diff --git a/web/apps/admin/src/pages/organizations/details/side-panel/side-panel.module.css b/web/sdk/admin/views/organizations/details/side-panel/side-panel.module.css similarity index 100% rename from web/apps/admin/src/pages/organizations/details/side-panel/side-panel.module.css rename to web/sdk/admin/views/organizations/details/side-panel/side-panel.module.css diff --git a/web/apps/admin/src/pages/organizations/details/side-panel/tokens-details-section.tsx b/web/sdk/admin/views/organizations/details/side-panel/tokens-details-section.tsx similarity index 93% rename from web/apps/admin/src/pages/organizations/details/side-panel/tokens-details-section.tsx rename to web/sdk/admin/views/organizations/details/side-panel/tokens-details-section.tsx index 63013d6cb..bf819818b 100644 --- a/web/apps/admin/src/pages/organizations/details/side-panel/tokens-details-section.tsx +++ b/web/sdk/admin/views/organizations/details/side-panel/tokens-details-section.tsx @@ -1,7 +1,6 @@ import { List, Text, Flex } from "@raystack/apsara"; import styles from "./side-panel.module.css"; -import CoinIcon from "~/assets/icons/coin.svg?react"; -import CoinColoredIcon from "~/assets/icons/coin-colored.svg?react"; +import { MixIcon } from "@radix-ui/react-icons"; import { useContext, useEffect } from "react"; import Skeleton from "react-loading-skeleton"; import { OrganizationContext } from "../contexts/organization-context"; @@ -53,7 +52,7 @@ export const TokensDetailsSection = () => { ) : ( - + {tokenBalance} )} @@ -68,7 +67,7 @@ export const TokensDetailsSection = () => { ) : ( - + {tokensUsed} )} diff --git a/web/apps/admin/src/pages/organizations/details/tokens/columns.tsx b/web/sdk/admin/views/organizations/details/tokens/columns.tsx similarity index 95% rename from web/apps/admin/src/pages/organizations/details/tokens/columns.tsx rename to web/sdk/admin/views/organizations/details/tokens/columns.tsx index 0d3bb882e..c9f78f3e0 100644 --- a/web/apps/admin/src/pages/organizations/details/tokens/columns.tsx +++ b/web/sdk/admin/views/organizations/details/tokens/columns.tsx @@ -1,5 +1,5 @@ import styles from "./tokens.module.css"; -import { NULL_DATE } from "~/utils/constants"; +import { NULL_DATE } from "../../../../utils/constants"; import dayjs from "dayjs"; import { Avatar, @@ -15,7 +15,7 @@ import { isNullTimestamp, TimeStamp, timestampToDate, -} from "~/utils/connect-timestamp"; +} from "../../../../utils/connect-timestamp"; export const getColumns = (): DataTableColumnDef< SearchOrganizationTokensResponse_OrganizationToken, diff --git a/web/apps/admin/src/pages/organizations/details/tokens/index.tsx b/web/sdk/admin/views/organizations/details/tokens/index.tsx similarity index 94% rename from web/apps/admin/src/pages/organizations/details/tokens/index.tsx rename to web/sdk/admin/views/organizations/details/tokens/index.tsx index 2bc97423e..ec58acf76 100644 --- a/web/apps/admin/src/pages/organizations/details/tokens/index.tsx +++ b/web/sdk/admin/views/organizations/details/tokens/index.tsx @@ -5,14 +5,11 @@ import { CoinIcon } from "@raystack/apsara/icons"; import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; import { useContext, useEffect, useMemo, useState } from "react"; import { OrganizationContext } from "../contexts/organization-context"; -import PageTitle from "~/components/page-title"; +import { PageTitle } from "../../../../components/PageTitle"; import { AdminServiceQueries } from "@raystack/proton/frontier"; import { useInfiniteQuery } from "@connectrpc/connect-query"; -import { - transformDataTableQueryToRQLRequest, - getConnectNextPageParam, - DEFAULT_PAGE_SIZE, -} from "@raystack/frontier/admin"; +import { getConnectNextPageParam, DEFAULT_PAGE_SIZE } from "../../../../utils/connect-pagination"; +import { transformDataTableQueryToRQLRequest } from "../../../../utils/transform-query"; import { getColumns } from "./columns"; import { useDebounceValue } from "usehooks-ts"; diff --git a/web/apps/admin/src/pages/organizations/details/tokens/tokens.module.css b/web/sdk/admin/views/organizations/details/tokens/tokens.module.css similarity index 100% rename from web/apps/admin/src/pages/organizations/details/tokens/tokens.module.css rename to web/sdk/admin/views/organizations/details/tokens/tokens.module.css diff --git a/web/apps/admin/src/pages/organizations/details/types.ts b/web/sdk/admin/views/organizations/details/types.ts similarity index 100% rename from web/apps/admin/src/pages/organizations/details/types.ts rename to web/sdk/admin/views/organizations/details/types.ts diff --git a/web/apps/admin/src/pages/organizations/list/columns.tsx b/web/sdk/admin/views/organizations/list/columns.tsx similarity index 99% rename from web/apps/admin/src/pages/organizations/list/columns.tsx rename to web/sdk/admin/views/organizations/list/columns.tsx index 40e08dceb..b3b57abeb 100644 --- a/web/apps/admin/src/pages/organizations/list/columns.tsx +++ b/web/sdk/admin/views/organizations/list/columns.tsx @@ -14,7 +14,7 @@ import { isNullTimestamp, TimeStamp, timestampToDate, -} from "~/utils/connect-timestamp"; +} from "../../../utils/connect-timestamp"; import dayjs from "dayjs"; import styles from "./list.module.css"; diff --git a/web/apps/admin/src/pages/organizations/list/create.tsx b/web/sdk/admin/views/organizations/list/create.tsx similarity index 91% rename from web/apps/admin/src/pages/organizations/list/create.tsx rename to web/sdk/admin/views/organizations/list/create.tsx index d874a0da4..72dae3b50 100644 --- a/web/apps/admin/src/pages/organizations/list/create.tsx +++ b/web/sdk/admin/views/organizations/list/create.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import styles from "./list.module.css"; import { Button, @@ -12,14 +12,12 @@ import { Label, } from "@raystack/apsara"; import { Cross1Icon } from "@radix-ui/react-icons"; -import { AvatarUpload } from "@raystack/frontier/react"; -import { AppContext } from "~/contexts/App"; +import { AvatarUpload } from "../../../../react/components/avatar-upload"; import { z } from "zod"; import { Controller, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation } from "@connectrpc/connect-query"; import { AdminServiceQueries } from "@raystack/proton/frontier"; -import { useNavigate } from "react-router-dom"; const orgCreateSchema = z .object({ @@ -43,25 +41,31 @@ const orgCreateSchema = z type OrgCreateSchema = z.infer; -async function loadCountries() { - const data = await import("~/assets/data/countries.json"); - return data.default; -} - const otherTypePrefix = "Other - "; -export function CreateOrganizationPanel({ onClose }: { onClose: () => void }) { - const { config } = useContext(AppContext); - const [countries, setCountries] = useState([]); - const navigate = useNavigate(); +export type CreateOrganizationPanelProps = { + onClose: () => void; + organizationTypes?: string[]; + appUrl?: string; + countries?: string[]; + onSuccess?: (orgId: string) => void; +}; - const industries = config?.organization_types || []; +export function CreateOrganizationPanel({ + onClose, + organizationTypes = [], + appUrl = "", + countries: countriesProp = [], + onSuccess, +}: CreateOrganizationPanelProps) { + const [countries, setCountries] = useState(countriesProp); + const industries = organizationTypes; useEffect(() => { - loadCountries().then((list) => - setCountries(list.map((country) => country.name)), - ); - }, []); + if (countriesProp.length > 0) { + setCountries(countriesProp); + } + }, [countriesProp]); const { handleSubmit, @@ -109,8 +113,8 @@ export function CreateOrganizationPanel({ onClose }: { onClose: () => void }) { const orgResp = await createOrganization({ body: payload }); const organization = orgResp.organization; - if (organization) { - navigate(`/organizations/${organization.id}`); + if (organization?.id) { + onSuccess?.(organization.id); } } catch (err: unknown) { console.error("Unable to create new org:", err); @@ -173,7 +177,7 @@ export function CreateOrganizationPanel({ onClose }: { onClose: () => void }) { /> { + return ( + } + /> + ); +}; + +const DEFAULT_SORT: DataTableSort = { name: "created_at", order: "desc" }; +const INITIAL_QUERY: DataTableQuery = { + offset: 0, + limit: DEFAULT_PAGE_SIZE, +}; + +export type OrganizationListProps = { + appName?: string; + onNavigateToOrg?: (id: string) => void; + onExportCsv?: () => Promise; + organizationTypes?: string[]; + appUrl?: string; + countries?: string[]; +}; + +export const OrganizationList = ({ + appName, + onNavigateToOrg, + onExportCsv, + organizationTypes = [], + appUrl, + countries = [], +}: OrganizationListProps = {}) => { + const [showCreatePanel, setShowCreatePanel] = useState(false); + + const [tableQuery, setTableQuery] = useDebouncedState( + INITIAL_QUERY, + 200, + ); + + // Transform the DataTableQuery to RQLRequest format + const query = transformDataTableQueryToRQLRequest(tableQuery, { + fieldNameMapping: { + createdBy: "created_by", + planName: "plan_name", + subscriptionCycleEndAt: "subscription_cycle_end_at", + paymentMode: "payment_mode", + subscriptionState: "subscription_state", + createdAt: "created_at", + }, + }); + + const { + data: infiniteData, + isLoading, + isFetchingNextPage, + fetchNextPage, + error, + isError, + } = useInfiniteQuery( + AdminServiceQueries.searchOrganizations, + { query: query }, + { + pageParamKey: "query", + getNextPageParam: lastPage => + getConnectNextPageParam(lastPage, { query: query }, "organizations"), + staleTime: 0, + refetchOnWindowFocus: false, + retry: 1, + retryDelay: 1000, + }, + ); + + const { data: plans = [], isLoading: isPlansLoading, error: plansError } = useQuery( + FrontierServiceQueries.listPlans, + create(ListPlansRequestSchema, {}), + { + select: (data) => data?.plans || [], + }, + ); + + // Log error if it occurs + useEffect(() => { + if (plansError) { + console.error("Failed to fetch plans:", plansError); + } + }, [plansError]); + + const data = + infiniteData?.pages?.flatMap(page => page?.organizations || []) || []; + + const groupCountMap = infiniteData + ? getGroupCountMapFromFirstPage(infiniteData) + : {}; + + const onTableQueryChange = (newQuery: DataTableQuery) => { + setTableQuery({ + ...newQuery, + offset: 0, + limit: newQuery.limit || DEFAULT_PAGE_SIZE, + }); + }; + + const handleLoadMore = async () => { + try { + await fetchNextPage(); + } catch (error) { + console.error("Error loading more organizations:", error); + } + }; + + function closeCreateOrgPanel() { + setShowCreatePanel(false); + } + + function openCreateOrgPanel() { + setShowCreatePanel(true); + } + + const columns = getColumns({ plans, groupCountMap }); + + const loading = isLoading || isPlansLoading || isFetchingNextPage; + + if (isError) { + return ( + <> + + } + heading="Error Loading Organizations" + subHeading={ + error?.message || + "Something went wrong while loading organizations. Please try again." + } + /> + + ); + } + + const tableClassName = + data.length || loading ? styles["table"] : styles["table-empty"]; + + function onRowClick(row: SearchOrganizationsResponse_OrganizationResult) { + if (row.id && onNavigateToOrg) onNavigateToOrg(row.id); + } + return ( + <> + {showCreatePanel ? ( + { + closeCreateOrgPanel(); + onNavigateToOrg?.(id); + }} + /> + ) : null} + + + + + + } + rowHeight={48} + groupHeaderHeight={48} + /> + + + + ); +}; diff --git a/web/apps/admin/src/pages/organizations/list/list.module.css b/web/sdk/admin/views/organizations/list/list.module.css similarity index 100% rename from web/apps/admin/src/pages/organizations/list/list.module.css rename to web/sdk/admin/views/organizations/list/list.module.css diff --git a/web/apps/admin/src/pages/organizations/list/navbar.tsx b/web/sdk/admin/views/organizations/list/navbar.tsx similarity index 78% rename from web/apps/admin/src/pages/organizations/list/navbar.tsx rename to web/sdk/admin/views/organizations/list/navbar.tsx index 8139226bf..153532942 100644 --- a/web/apps/admin/src/pages/organizations/list/navbar.tsx +++ b/web/sdk/admin/views/organizations/list/navbar.tsx @@ -15,19 +15,17 @@ import { PlusIcon, } from "@radix-ui/react-icons"; import React, { useState } from "react"; -import { exportCsvFromStream } from "~/utils/helper"; -import { clients } from "~/connect/clients"; - -const adminClient = clients.admin({ useBinary: true }); interface OrganizationsNavabarProps { searchQuery?: string; openCreatePanel: () => void; + onExportCsv?: () => Promise; } export const OrganizationsNavabar = ({ searchQuery, openCreatePanel, + onExportCsv, }: OrganizationsNavabarProps) => { const [showSearch, setShowSearch] = useState(searchQuery ? true : false); const [isDownloading, setIsDownloading] = useState(false); @@ -44,13 +42,10 @@ export const OrganizationsNavabar = ({ } async function onDownloadClick() { + if (!onExportCsv) return; try { setIsDownloading(true); - await exportCsvFromStream( - adminClient.exportOrganizations, - {}, - "organizations.csv" - ); + await onExportCsv(); } catch (error) { console.error(error); } finally { @@ -93,15 +88,17 @@ export const OrganizationsNavabar = ({ )} - - {isDownloading ? : } - + {onExportCsv ? ( + + {isDownloading ? : } + + ) : null} ); diff --git a/web/lib/admin/views/plans/columns.tsx b/web/sdk/admin/views/plans/columns.tsx similarity index 100% rename from web/lib/admin/views/plans/columns.tsx rename to web/sdk/admin/views/plans/columns.tsx diff --git a/web/lib/admin/views/plans/details.tsx b/web/sdk/admin/views/plans/details.tsx similarity index 100% rename from web/lib/admin/views/plans/details.tsx rename to web/sdk/admin/views/plans/details.tsx diff --git a/web/lib/admin/views/plans/header.tsx b/web/sdk/admin/views/plans/header.tsx similarity index 100% rename from web/lib/admin/views/plans/header.tsx rename to web/sdk/admin/views/plans/header.tsx diff --git a/web/lib/admin/views/plans/index.tsx b/web/sdk/admin/views/plans/index.tsx similarity index 100% rename from web/lib/admin/views/plans/index.tsx rename to web/sdk/admin/views/plans/index.tsx diff --git a/web/lib/admin/views/plans/plans.module.css b/web/sdk/admin/views/plans/plans.module.css similarity index 100% rename from web/lib/admin/views/plans/plans.module.css rename to web/sdk/admin/views/plans/plans.module.css diff --git a/web/lib/admin/views/preferences/PreferencesView.tsx b/web/sdk/admin/views/preferences/PreferencesView.tsx similarity index 100% rename from web/lib/admin/views/preferences/PreferencesView.tsx rename to web/sdk/admin/views/preferences/PreferencesView.tsx diff --git a/web/lib/admin/views/preferences/columns.tsx b/web/sdk/admin/views/preferences/columns.tsx similarity index 100% rename from web/lib/admin/views/preferences/columns.tsx rename to web/sdk/admin/views/preferences/columns.tsx diff --git a/web/lib/admin/views/preferences/details.tsx b/web/sdk/admin/views/preferences/details.tsx similarity index 100% rename from web/lib/admin/views/preferences/details.tsx rename to web/sdk/admin/views/preferences/details.tsx diff --git a/web/lib/admin/views/preferences/index.tsx b/web/sdk/admin/views/preferences/index.tsx similarity index 100% rename from web/lib/admin/views/preferences/index.tsx rename to web/sdk/admin/views/preferences/index.tsx diff --git a/web/lib/admin/views/preferences/layout.tsx b/web/sdk/admin/views/preferences/layout.tsx similarity index 100% rename from web/lib/admin/views/preferences/layout.tsx rename to web/sdk/admin/views/preferences/layout.tsx diff --git a/web/lib/admin/views/preferences/preferences.module.css b/web/sdk/admin/views/preferences/preferences.module.css similarity index 100% rename from web/lib/admin/views/preferences/preferences.module.css rename to web/sdk/admin/views/preferences/preferences.module.css diff --git a/web/lib/admin/views/products/columns.tsx b/web/sdk/admin/views/products/columns.tsx similarity index 100% rename from web/lib/admin/views/products/columns.tsx rename to web/sdk/admin/views/products/columns.tsx diff --git a/web/lib/admin/views/products/details.tsx b/web/sdk/admin/views/products/details.tsx similarity index 100% rename from web/lib/admin/views/products/details.tsx rename to web/sdk/admin/views/products/details.tsx diff --git a/web/lib/admin/views/products/exports.ts b/web/sdk/admin/views/products/exports.ts similarity index 100% rename from web/lib/admin/views/products/exports.ts rename to web/sdk/admin/views/products/exports.ts diff --git a/web/lib/admin/views/products/header.tsx b/web/sdk/admin/views/products/header.tsx similarity index 100% rename from web/lib/admin/views/products/header.tsx rename to web/sdk/admin/views/products/header.tsx diff --git a/web/lib/admin/views/products/index.tsx b/web/sdk/admin/views/products/index.tsx similarity index 100% rename from web/lib/admin/views/products/index.tsx rename to web/sdk/admin/views/products/index.tsx diff --git a/web/lib/admin/views/products/prices/columns.tsx b/web/sdk/admin/views/products/prices/columns.tsx similarity index 100% rename from web/lib/admin/views/products/prices/columns.tsx rename to web/sdk/admin/views/products/prices/columns.tsx diff --git a/web/lib/admin/views/products/prices/index.tsx b/web/sdk/admin/views/products/prices/index.tsx similarity index 100% rename from web/lib/admin/views/products/prices/index.tsx rename to web/sdk/admin/views/products/prices/index.tsx diff --git a/web/lib/admin/views/products/products.module.css b/web/sdk/admin/views/products/products.module.css similarity index 100% rename from web/lib/admin/views/products/products.module.css rename to web/sdk/admin/views/products/products.module.css diff --git a/web/lib/admin/views/products/types.tsx b/web/sdk/admin/views/products/types.tsx similarity index 100% rename from web/lib/admin/views/products/types.tsx rename to web/sdk/admin/views/products/types.tsx diff --git a/web/lib/admin/views/roles/columns.tsx b/web/sdk/admin/views/roles/columns.tsx similarity index 100% rename from web/lib/admin/views/roles/columns.tsx rename to web/sdk/admin/views/roles/columns.tsx diff --git a/web/lib/admin/views/roles/details.tsx b/web/sdk/admin/views/roles/details.tsx similarity index 100% rename from web/lib/admin/views/roles/details.tsx rename to web/sdk/admin/views/roles/details.tsx diff --git a/web/lib/admin/views/roles/header.tsx b/web/sdk/admin/views/roles/header.tsx similarity index 100% rename from web/lib/admin/views/roles/header.tsx rename to web/sdk/admin/views/roles/header.tsx diff --git a/web/lib/admin/views/roles/index.tsx b/web/sdk/admin/views/roles/index.tsx similarity index 100% rename from web/lib/admin/views/roles/index.tsx rename to web/sdk/admin/views/roles/index.tsx diff --git a/web/lib/admin/views/roles/roles.module.css b/web/sdk/admin/views/roles/roles.module.css similarity index 100% rename from web/lib/admin/views/roles/roles.module.css rename to web/sdk/admin/views/roles/roles.module.css diff --git a/web/lib/admin/views/roles/types.tsx b/web/sdk/admin/views/roles/types.tsx similarity index 100% rename from web/lib/admin/views/roles/types.tsx rename to web/sdk/admin/views/roles/types.tsx diff --git a/web/sdk/admin/views/users/UsersView.tsx b/web/sdk/admin/views/users/UsersView.tsx new file mode 100644 index 000000000..2c016e22d --- /dev/null +++ b/web/sdk/admin/views/users/UsersView.tsx @@ -0,0 +1,27 @@ +import { UsersList } from "./list/list"; +import { UserDetailsByUserId } from "./details/user-details"; + +export type UsersViewProps = { + selectedUserId?: string; + onCloseDetail?: () => void; + onExportUsers?: () => Promise; + onNavigateToUser?: (userId: string) => void; +}; + +export default function UsersView({ + selectedUserId, + onCloseDetail, + onExportUsers, + onNavigateToUser, +}: UsersViewProps = {}) { + if (selectedUserId) { + return ; + } + + return ( + + ); +} diff --git a/web/apps/admin/src/pages/users/details/index.ts b/web/sdk/admin/views/users/details/index.ts similarity index 100% rename from web/apps/admin/src/pages/users/details/index.ts rename to web/sdk/admin/views/users/details/index.ts diff --git a/web/apps/admin/src/pages/users/details/layout/index.ts b/web/sdk/admin/views/users/details/layout/index.ts similarity index 100% rename from web/apps/admin/src/pages/users/details/layout/index.ts rename to web/sdk/admin/views/users/details/layout/index.ts diff --git a/web/apps/admin/src/pages/users/details/layout/layout.module.css b/web/sdk/admin/views/users/details/layout/layout.module.css similarity index 100% rename from web/apps/admin/src/pages/users/details/layout/layout.module.css rename to web/sdk/admin/views/users/details/layout/layout.module.css diff --git a/web/apps/admin/src/pages/users/details/layout/layout.tsx b/web/sdk/admin/views/users/details/layout/layout.tsx similarity index 100% rename from web/apps/admin/src/pages/users/details/layout/layout.tsx rename to web/sdk/admin/views/users/details/layout/layout.tsx diff --git a/web/apps/admin/src/pages/users/details/layout/membership-dropdown.tsx b/web/sdk/admin/views/users/details/layout/membership-dropdown.tsx similarity index 97% rename from web/apps/admin/src/pages/users/details/layout/membership-dropdown.tsx rename to web/sdk/admin/views/users/details/layout/membership-dropdown.tsx index b997d7d4d..ff74b30a0 100644 --- a/web/apps/admin/src/pages/users/details/layout/membership-dropdown.tsx +++ b/web/sdk/admin/views/users/details/layout/membership-dropdown.tsx @@ -11,8 +11,8 @@ import { } from "@raystack/proton/frontier"; import { create } from "@bufbuild/protobuf"; import { useQuery } from "@connectrpc/connect-query"; -import { SCOPES } from "~/utils/constants"; -import { AssignRole } from "~/components/assign-role"; +import { SCOPES } from "../../../../utils/constants"; +import { AssignRole } from "../../../../components/AssignRole"; import { useUser } from "../user-context"; import { SuspendUser } from "./suspend-user"; diff --git a/web/apps/admin/src/pages/users/details/layout/navbar.module.css b/web/sdk/admin/views/users/details/layout/navbar.module.css similarity index 100% rename from web/apps/admin/src/pages/users/details/layout/navbar.module.css rename to web/sdk/admin/views/users/details/layout/navbar.module.css diff --git a/web/apps/admin/src/pages/users/details/layout/navbar.tsx b/web/sdk/admin/views/users/details/layout/navbar.tsx similarity index 97% rename from web/apps/admin/src/pages/users/details/layout/navbar.tsx rename to web/sdk/admin/views/users/details/layout/navbar.tsx index da3595ca9..cfcd0f0b3 100644 --- a/web/apps/admin/src/pages/users/details/layout/navbar.tsx +++ b/web/sdk/admin/views/users/details/layout/navbar.tsx @@ -8,7 +8,7 @@ import { } from "@raystack/apsara"; import { NavLink } from "react-router-dom"; import { SidebarIcon } from "@raystack/apsara/icons"; -import UserIcon from "~/assets/icons/users.svg?react"; +import UserIcon from "../../../../assets/icons/UsersIcon"; import styles from "./navbar.module.css"; import { getUserName } from "../../util"; import { useUser } from "../user-context"; diff --git a/web/apps/admin/src/pages/users/details/layout/side-panel-details.tsx b/web/sdk/admin/views/users/details/layout/side-panel-details.tsx similarity index 95% rename from web/apps/admin/src/pages/users/details/layout/side-panel-details.tsx rename to web/sdk/admin/views/users/details/layout/side-panel-details.tsx index 00ea304aa..58805c576 100644 --- a/web/apps/admin/src/pages/users/details/layout/side-panel-details.tsx +++ b/web/sdk/admin/views/users/details/layout/side-panel-details.tsx @@ -3,7 +3,7 @@ import { CalendarIcon } from "@radix-ui/react-icons"; import styles from "./side-panel.module.css"; import { UserState, USER_STATES } from "../../util"; import { useUser } from "../user-context"; -import { timestampToDayjs } from "~/utils/connect-timestamp"; +import { timestampToDayjs } from "../../../../utils/connect-timestamp"; export const SidePanelDetails = () => { const { user } = useUser(); diff --git a/web/apps/admin/src/pages/users/details/layout/side-panel-membership.tsx b/web/sdk/admin/views/users/details/layout/side-panel-membership.tsx similarity index 96% rename from web/apps/admin/src/pages/users/details/layout/side-panel-membership.tsx rename to web/sdk/admin/views/users/details/layout/side-panel-membership.tsx index 5654e4fc7..3dc0831c3 100644 --- a/web/apps/admin/src/pages/users/details/layout/side-panel-membership.tsx +++ b/web/sdk/admin/views/users/details/layout/side-panel-membership.tsx @@ -5,7 +5,7 @@ import Skeleton from "react-loading-skeleton"; import { type SearchUserOrganizationsResponse_UserOrganization } from "@raystack/proton/frontier"; import styles from "./side-panel.module.css"; import { MembershipDropdown } from "./membership-dropdown"; -import { timestampToDate, isNullTimestamp } from "~/utils/connect-timestamp"; +import { timestampToDate, isNullTimestamp } from "../../../../utils/connect-timestamp"; interface SidePanelMembershipProps { data?: SearchUserOrganizationsResponse_UserOrganization; diff --git a/web/apps/admin/src/pages/users/details/layout/side-panel.module.css b/web/sdk/admin/views/users/details/layout/side-panel.module.css similarity index 100% rename from web/apps/admin/src/pages/users/details/layout/side-panel.module.css rename to web/sdk/admin/views/users/details/layout/side-panel.module.css diff --git a/web/apps/admin/src/pages/users/details/layout/side-panel.tsx b/web/sdk/admin/views/users/details/layout/side-panel.tsx similarity index 100% rename from web/apps/admin/src/pages/users/details/layout/side-panel.tsx rename to web/sdk/admin/views/users/details/layout/side-panel.tsx diff --git a/web/apps/admin/src/pages/users/details/layout/suspend-user.tsx b/web/sdk/admin/views/users/details/layout/suspend-user.tsx similarity index 100% rename from web/apps/admin/src/pages/users/details/layout/suspend-user.tsx rename to web/sdk/admin/views/users/details/layout/suspend-user.tsx diff --git a/web/apps/admin/src/pages/users/details/security/block-user.tsx b/web/sdk/admin/views/users/details/security/block-user.tsx similarity index 100% rename from web/apps/admin/src/pages/users/details/security/block-user.tsx rename to web/sdk/admin/views/users/details/security/block-user.tsx diff --git a/web/apps/admin/src/pages/users/details/security/index.ts b/web/sdk/admin/views/users/details/security/index.ts similarity index 100% rename from web/apps/admin/src/pages/users/details/security/index.ts rename to web/sdk/admin/views/users/details/security/index.ts diff --git a/web/apps/admin/src/pages/users/details/security/security.module.css b/web/sdk/admin/views/users/details/security/security.module.css similarity index 100% rename from web/apps/admin/src/pages/users/details/security/security.module.css rename to web/sdk/admin/views/users/details/security/security.module.css diff --git a/web/apps/admin/src/pages/users/details/security/security.tsx b/web/sdk/admin/views/users/details/security/security.tsx similarity index 89% rename from web/apps/admin/src/pages/users/details/security/security.tsx rename to web/sdk/admin/views/users/details/security/security.tsx index 60f6f487d..57598d028 100644 --- a/web/apps/admin/src/pages/users/details/security/security.tsx +++ b/web/sdk/admin/views/users/details/security/security.tsx @@ -1,11 +1,11 @@ import { Flex, Separator, Text } from "@raystack/apsara"; -import PageTitle from "~/components/page-title"; +import { PageTitle } from "../../../../components/PageTitle"; import { useUser } from "../user-context"; import { BlockUserDialog } from "./block-user"; import { UserSessions } from "./sessions"; import styles from "./security.module.css"; -export const UserDetailsSecurityPage = () => { +export const UserDetailsSecurityContent = () => { const { user } = useUser(); const title = `Security | ${user?.email} | Users`; diff --git a/web/apps/admin/src/pages/users/details/security/sessions/index.tsx b/web/sdk/admin/views/users/details/security/sessions/index.tsx similarity index 98% rename from web/apps/admin/src/pages/users/details/security/sessions/index.tsx rename to web/sdk/admin/views/users/details/security/sessions/index.tsx index 1dd5442cb..a8480a43a 100644 --- a/web/apps/admin/src/pages/users/details/security/sessions/index.tsx +++ b/web/sdk/admin/views/users/details/security/sessions/index.tsx @@ -8,7 +8,7 @@ import { useQueryClient } from "@tanstack/react-query"; import { AdminServiceQueries, Session } from "@raystack/proton/frontier"; import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; -import { timestampToDate } from "~/utils/connect-timestamp"; +import { timestampToDate } from "../../../../../utils/connect-timestamp"; import styles from "./sessions.module.css"; /** diff --git a/web/apps/admin/src/pages/users/details/security/sessions/revoke-session-confirm.tsx b/web/sdk/admin/views/users/details/security/sessions/revoke-session-confirm.tsx similarity index 100% rename from web/apps/admin/src/pages/users/details/security/sessions/revoke-session-confirm.tsx rename to web/sdk/admin/views/users/details/security/sessions/revoke-session-confirm.tsx diff --git a/web/apps/admin/src/pages/users/details/security/sessions/revoke-session-final-confirm.tsx b/web/sdk/admin/views/users/details/security/sessions/revoke-session-final-confirm.tsx similarity index 100% rename from web/apps/admin/src/pages/users/details/security/sessions/revoke-session-final-confirm.tsx rename to web/sdk/admin/views/users/details/security/sessions/revoke-session-final-confirm.tsx diff --git a/web/apps/admin/src/pages/users/details/security/sessions/session-skeleton.tsx b/web/sdk/admin/views/users/details/security/sessions/session-skeleton.tsx similarity index 100% rename from web/apps/admin/src/pages/users/details/security/sessions/session-skeleton.tsx rename to web/sdk/admin/views/users/details/security/sessions/session-skeleton.tsx diff --git a/web/apps/admin/src/pages/users/details/security/sessions/sessions.module.css b/web/sdk/admin/views/users/details/security/sessions/sessions.module.css similarity index 100% rename from web/apps/admin/src/pages/users/details/security/sessions/sessions.module.css rename to web/sdk/admin/views/users/details/security/sessions/sessions.module.css diff --git a/web/apps/admin/src/pages/users/details/user-context.tsx b/web/sdk/admin/views/users/details/user-context.tsx similarity index 100% rename from web/apps/admin/src/pages/users/details/user-context.tsx rename to web/sdk/admin/views/users/details/user-context.tsx diff --git a/web/apps/admin/src/pages/users/details/user-details.tsx b/web/sdk/admin/views/users/details/user-details.tsx similarity index 52% rename from web/apps/admin/src/pages/users/details/user-details.tsx rename to web/sdk/admin/views/users/details/user-details.tsx index 92a522046..cb2b1df19 100644 --- a/web/apps/admin/src/pages/users/details/user-details.tsx +++ b/web/sdk/admin/views/users/details/user-details.tsx @@ -1,16 +1,33 @@ -import { Outlet, useParams } from "react-router-dom"; -import { Flex, EmptyState } from "@raystack/apsara"; -import LoadingState from "~/components/states/Loading"; -import PageTitle from "~/components/page-title"; -import UserIcon from "~/assets/icons/users.svg?react"; +import { Flex, EmptyState, Spinner } from "@raystack/apsara"; +import { PageTitle } from "../../../components/PageTitle"; +import UserIcon from "../../../assets/icons/UsersIcon"; import { UserDetailsLayout } from "./layout"; import { UserProvider } from "./user-context"; import { useQuery } from "@connectrpc/connect-query"; +import type { User } from "@raystack/proton/frontier"; import { AdminServiceQueries } from "@raystack/proton/frontier"; +import { UserDetailsSecurityContent } from "./security/security"; -export const UserDetails = () => { - const { userId } = useParams(); +interface UserDetailContentProps { + user: User; + refetch: () => void; +} +export const UserDetailContent = ({ user, refetch }: UserDetailContentProps) => { + return ( + + + + + + ); +}; + +interface UserDetailsByUserIdProps { + userId: string; +} + +export const UserDetailsByUserId = ({ userId }: UserDetailsByUserIdProps) => { const { data, isLoading, refetch } = useQuery( AdminServiceQueries.searchUsers, { query: { search: userId } }, @@ -25,15 +42,20 @@ export const UserDetails = () => { const user = data?.users?.[0]; if (isLoading) { - return ; + return ( + + + + ); } - if (!user?.id) + if (!user?.id) { return ( + justify="center" + > } @@ -42,12 +64,7 @@ export const UserDetails = () => { /> ); + } - return ( - - - - - - ); + return ; }; diff --git a/web/apps/admin/src/pages/users/list/columns.tsx b/web/sdk/admin/views/users/list/columns.tsx similarity index 80% rename from web/apps/admin/src/pages/users/list/columns.tsx rename to web/sdk/admin/views/users/list/columns.tsx index 40c440202..122ea7abb 100644 --- a/web/apps/admin/src/pages/users/list/columns.tsx +++ b/web/sdk/admin/views/users/list/columns.tsx @@ -6,6 +6,7 @@ import { getAvatarColor, Text, } from "@raystack/apsara"; +import { Link } from "react-router-dom"; import dayjs from "dayjs"; import styles from "./list.module.css"; import { getUserName, USER_STATES, UserState } from "../util"; @@ -14,7 +15,7 @@ import { isNullTimestamp, TimeStamp, timestampToDate, -} from "~/utils/connect-timestamp"; +} from "../../../utils/connect-timestamp"; interface getColumnsOptions { groupCountMap: Record>; @@ -34,15 +35,18 @@ export const getColumns = ({ cell: ({ row }) => { const avatarColor = getAvatarColor(row?.original?.id || ""); const name = getUserName(row.original); + const userId = row.original.id; return ( - - - {name} - + + + + {name} + + ); }, enableColumnFilter: true, diff --git a/web/apps/admin/src/pages/users/list/index.ts b/web/sdk/admin/views/users/list/index.ts similarity index 100% rename from web/apps/admin/src/pages/users/list/index.ts rename to web/sdk/admin/views/users/list/index.ts diff --git a/web/apps/admin/src/pages/users/list/invite-users.module.css b/web/sdk/admin/views/users/list/invite-users.module.css similarity index 100% rename from web/apps/admin/src/pages/users/list/invite-users.module.css rename to web/sdk/admin/views/users/list/invite-users.module.css diff --git a/web/apps/admin/src/pages/users/list/invite-users.tsx b/web/sdk/admin/views/users/list/invite-users.tsx similarity index 99% rename from web/apps/admin/src/pages/users/list/invite-users.tsx rename to web/sdk/admin/views/users/list/invite-users.tsx index 8cd9da965..306e049b8 100644 --- a/web/apps/admin/src/pages/users/list/invite-users.tsx +++ b/web/sdk/admin/views/users/list/invite-users.tsx @@ -13,7 +13,7 @@ import { PlusIcon } from "@radix-ui/react-icons"; import * as z from "zod"; import { Controller, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; -import { SCOPES, DEFAULT_ROLES } from "~/utils/constants"; +import { SCOPES, DEFAULT_ROLES } from "../../../utils/constants"; import styles from "./invite-users.module.css"; import Skeleton from "react-loading-skeleton"; import { useMutation, useQuery } from "@connectrpc/connect-query"; diff --git a/web/apps/admin/src/pages/users/list/list.module.css b/web/sdk/admin/views/users/list/list.module.css similarity index 100% rename from web/apps/admin/src/pages/users/list/list.module.css rename to web/sdk/admin/views/users/list/list.module.css diff --git a/web/apps/admin/src/pages/users/list/list.tsx b/web/sdk/admin/views/users/list/list.tsx similarity index 87% rename from web/apps/admin/src/pages/users/list/list.tsx rename to web/sdk/admin/views/users/list/list.tsx index 05d6d3a5b..96ce9516c 100644 --- a/web/apps/admin/src/pages/users/list/list.tsx +++ b/web/sdk/admin/views/users/list/list.tsx @@ -3,17 +3,16 @@ import type { DataTableQuery, DataTableSort } from "@raystack/apsara"; import Navbar from "./navbar"; import styles from "./list.module.css"; import { getColumns } from "./columns"; -import { useNavigate } from "react-router-dom"; -import PageTitle from "~/components/page-title"; -import UserIcon from "~/assets/icons/users.svg?react"; +import { PageTitle } from "../../../components/PageTitle"; +import UserIcon from "../../../assets/icons/UsersIcon"; import { useInfiniteQuery } from "@connectrpc/connect-query"; import { AdminServiceQueries, type User } from "@raystack/proton/frontier"; import { getConnectNextPageParam, getGroupCountMapFromFirstPage, DEFAULT_PAGE_SIZE, - transformDataTableQueryToRQLRequest, -} from "@raystack/frontier/admin"; +} from "../../../utils/connect-pagination"; +import { transformDataTableQueryToRQLRequest } from "../../../utils/transform-query"; import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; import { useDebouncedState } from "@raystack/apsara/hooks"; @@ -37,8 +36,12 @@ const INITIAL_QUERY: DataTableQuery = { limit: DEFAULT_PAGE_SIZE, }; -export const UsersList = () => { - const navigate = useNavigate(); +interface UsersListProps { + onExportUsers?: () => Promise; + onNavigateToUser?: (userId: string) => void; +} + +export const UsersList = ({ onExportUsers, onNavigateToUser }: UsersListProps) => { const [tableQuery, setTableQuery] = useDebouncedState( INITIAL_QUERY, 200, @@ -100,7 +103,7 @@ export const UsersList = () => { const loading = isLoading || isFetchingNextPage; const onRowClick = (row: User) => { - navigate(`/users/${row.id}`); + onNavigateToUser?.(row.id); }; if (isError) { @@ -137,7 +140,7 @@ export const UsersList = () => { onLoadMore={handleLoadMore} onRowClick={onRowClick}> - + Promise; } -const Navbar = ({ searchQuery }: NavbarProps) => { +const Navbar = ({ searchQuery, onExportUsers }: NavbarProps) => { const [showSearch, setShowSearch] = useState(searchQuery ? true : false); const [isDownloading, setIsDownloading] = useState(false); @@ -36,9 +33,10 @@ const Navbar = ({ searchQuery }: NavbarProps) => { } async function onDownloadClick() { + if (!onExportUsers) return; try { setIsDownloading(true); - await exportCsvFromStream(adminClient.exportUsers, {}, "users.csv"); + await onExportUsers(); } catch (error) { console.error(error); } finally { @@ -79,7 +77,7 @@ const Navbar = ({ searchQuery }: NavbarProps) => { aria-label="Download" data-test-id="admin-download-users-list-btn" onClick={onDownloadClick} - disabled={isDownloading} + disabled={isDownloading || !onExportUsers} > {isDownloading ? : } diff --git a/web/apps/admin/src/pages/users/util.ts b/web/sdk/admin/views/users/util.ts similarity index 100% rename from web/apps/admin/src/pages/users/util.ts rename to web/sdk/admin/views/users/util.ts diff --git a/web/lib/admin/views/webhooks/webhooks/columns.tsx b/web/sdk/admin/views/webhooks/webhooks/columns.tsx similarity index 100% rename from web/lib/admin/views/webhooks/webhooks/columns.tsx rename to web/sdk/admin/views/webhooks/webhooks/columns.tsx diff --git a/web/lib/admin/views/webhooks/webhooks/create/index.tsx b/web/sdk/admin/views/webhooks/webhooks/create/index.tsx similarity index 100% rename from web/lib/admin/views/webhooks/webhooks/create/index.tsx rename to web/sdk/admin/views/webhooks/webhooks/create/index.tsx diff --git a/web/lib/admin/views/webhooks/webhooks/delete/index.tsx b/web/sdk/admin/views/webhooks/webhooks/delete/index.tsx similarity index 100% rename from web/lib/admin/views/webhooks/webhooks/delete/index.tsx rename to web/sdk/admin/views/webhooks/webhooks/delete/index.tsx diff --git a/web/lib/admin/views/webhooks/webhooks/header.tsx b/web/sdk/admin/views/webhooks/webhooks/header.tsx similarity index 100% rename from web/lib/admin/views/webhooks/webhooks/header.tsx rename to web/sdk/admin/views/webhooks/webhooks/header.tsx diff --git a/web/lib/admin/views/webhooks/webhooks/hooks/useWebhookQueries.ts b/web/sdk/admin/views/webhooks/webhooks/hooks/useWebhookQueries.ts similarity index 100% rename from web/lib/admin/views/webhooks/webhooks/hooks/useWebhookQueries.ts rename to web/sdk/admin/views/webhooks/webhooks/hooks/useWebhookQueries.ts diff --git a/web/lib/admin/views/webhooks/webhooks/index.tsx b/web/sdk/admin/views/webhooks/webhooks/index.tsx similarity index 100% rename from web/lib/admin/views/webhooks/webhooks/index.tsx rename to web/sdk/admin/views/webhooks/webhooks/index.tsx diff --git a/web/lib/admin/views/webhooks/webhooks/update/index.tsx b/web/sdk/admin/views/webhooks/webhooks/update/index.tsx similarity index 100% rename from web/lib/admin/views/webhooks/webhooks/update/index.tsx rename to web/sdk/admin/views/webhooks/webhooks/update/index.tsx diff --git a/web/lib/admin/views/webhooks/webhooks/webhooks.module.css b/web/sdk/admin/views/webhooks/webhooks/webhooks.module.css similarity index 100% rename from web/lib/admin/views/webhooks/webhooks/webhooks.module.css rename to web/sdk/admin/views/webhooks/webhooks/webhooks.module.css diff --git a/web/lib/custom.d.ts b/web/sdk/custom.d.ts similarity index 100% rename from web/lib/custom.d.ts rename to web/sdk/custom.d.ts diff --git a/web/lib/hooks/index.ts b/web/sdk/hooks/index.ts similarity index 100% rename from web/lib/hooks/index.ts rename to web/sdk/hooks/index.ts diff --git a/web/lib/jest.config.js b/web/sdk/jest.config.js similarity index 100% rename from web/lib/jest.config.js rename to web/sdk/jest.config.js diff --git a/web/lib/package.json b/web/sdk/package.json similarity index 99% rename from web/lib/package.json rename to web/sdk/package.json index 76c45d2fb..caa0d6850 100644 --- a/web/lib/package.json +++ b/web/sdk/package.json @@ -95,6 +95,7 @@ "@stitches/react": "^1.2.8", "react-loading-skeleton": "^3.4.0", "sonner": "^1.4.41", + "usehooks-ts": "^3.1.1", "zod": "^3.22.3" }, "dependencies": { diff --git a/web/lib/react/assets/bell-slash.svg b/web/sdk/react/assets/bell-slash.svg similarity index 100% rename from web/lib/react/assets/bell-slash.svg rename to web/sdk/react/assets/bell-slash.svg diff --git a/web/lib/react/assets/bell.svg b/web/sdk/react/assets/bell.svg similarity index 100% rename from web/lib/react/assets/bell.svg rename to web/sdk/react/assets/bell.svg diff --git a/web/lib/react/assets/check-circle.svg b/web/sdk/react/assets/check-circle.svg similarity index 100% rename from web/lib/react/assets/check-circle.svg rename to web/sdk/react/assets/check-circle.svg diff --git a/web/lib/react/assets/chevron-left.svg b/web/sdk/react/assets/chevron-left.svg similarity index 100% rename from web/lib/react/assets/chevron-left.svg rename to web/sdk/react/assets/chevron-left.svg diff --git a/web/lib/react/assets/close-close.svg b/web/sdk/react/assets/close-close.svg similarity index 100% rename from web/lib/react/assets/close-close.svg rename to web/sdk/react/assets/close-close.svg diff --git a/web/lib/react/assets/close-default.svg b/web/sdk/react/assets/close-default.svg similarity index 100% rename from web/lib/react/assets/close-default.svg rename to web/sdk/react/assets/close-default.svg diff --git a/web/lib/react/assets/close.svg b/web/sdk/react/assets/close.svg similarity index 100% rename from web/lib/react/assets/close.svg rename to web/sdk/react/assets/close.svg diff --git a/web/lib/react/assets/coin.svg b/web/sdk/react/assets/coin.svg similarity index 100% rename from web/lib/react/assets/coin.svg rename to web/sdk/react/assets/coin.svg diff --git a/web/lib/react/assets/cross.svg b/web/sdk/react/assets/cross.svg similarity index 100% rename from web/lib/react/assets/cross.svg rename to web/sdk/react/assets/cross.svg diff --git a/web/lib/react/assets/exclamation-triangle.svg b/web/sdk/react/assets/exclamation-triangle.svg similarity index 100% rename from web/lib/react/assets/exclamation-triangle.svg rename to web/sdk/react/assets/exclamation-triangle.svg diff --git a/web/lib/react/assets/key.svg b/web/sdk/react/assets/key.svg similarity index 100% rename from web/lib/react/assets/key.svg rename to web/sdk/react/assets/key.svg diff --git a/web/lib/react/assets/line.svg b/web/sdk/react/assets/line.svg similarity index 100% rename from web/lib/react/assets/line.svg rename to web/sdk/react/assets/line.svg diff --git a/web/lib/react/assets/logo.png b/web/sdk/react/assets/logo.png similarity index 100% rename from web/lib/react/assets/logo.png rename to web/sdk/react/assets/logo.png diff --git a/web/lib/react/assets/logos/google-logo.svg b/web/sdk/react/assets/logos/google-logo.svg similarity index 100% rename from web/lib/react/assets/logos/google-logo.svg rename to web/sdk/react/assets/logos/google-logo.svg diff --git a/web/lib/react/assets/open.svg b/web/sdk/react/assets/open.svg similarity index 100% rename from web/lib/react/assets/open.svg rename to web/sdk/react/assets/open.svg diff --git a/web/lib/react/assets/organization.png b/web/sdk/react/assets/organization.png similarity index 100% rename from web/lib/react/assets/organization.png rename to web/sdk/react/assets/organization.png diff --git a/web/lib/react/assets/resize-collapse.svg b/web/sdk/react/assets/resize-collapse.svg similarity index 100% rename from web/lib/react/assets/resize-collapse.svg rename to web/sdk/react/assets/resize-collapse.svg diff --git a/web/lib/react/assets/resize-default.svg b/web/sdk/react/assets/resize-default.svg similarity index 100% rename from web/lib/react/assets/resize-default.svg rename to web/sdk/react/assets/resize-default.svg diff --git a/web/lib/react/assets/resize-expand.svg b/web/sdk/react/assets/resize-expand.svg similarity index 100% rename from web/lib/react/assets/resize-expand.svg rename to web/sdk/react/assets/resize-expand.svg diff --git a/web/lib/react/assets/user.png b/web/sdk/react/assets/user.png similarity index 100% rename from web/lib/react/assets/user.png rename to web/sdk/react/assets/user.png diff --git a/web/lib/react/assets/users.svg b/web/sdk/react/assets/users.svg similarity index 100% rename from web/lib/react/assets/users.svg rename to web/sdk/react/assets/users.svg diff --git a/web/lib/react/components/Container.tsx b/web/sdk/react/components/Container.tsx similarity index 100% rename from web/lib/react/components/Container.tsx rename to web/sdk/react/components/Container.tsx diff --git a/web/lib/react/components/Header.tsx b/web/sdk/react/components/Header.tsx similarity index 100% rename from web/lib/react/components/Header.tsx rename to web/sdk/react/components/Header.tsx diff --git a/web/lib/react/components/Layout/index.tsx b/web/sdk/react/components/Layout/index.tsx similarity index 100% rename from web/lib/react/components/Layout/index.tsx rename to web/sdk/react/components/Layout/index.tsx diff --git a/web/lib/react/components/Layout/layout.module.css b/web/sdk/react/components/Layout/layout.module.css similarity index 100% rename from web/lib/react/components/Layout/layout.module.css rename to web/sdk/react/components/Layout/layout.module.css diff --git a/web/lib/react/components/avatar-upload/avatar-upload.module.css b/web/sdk/react/components/avatar-upload/avatar-upload.module.css similarity index 100% rename from web/lib/react/components/avatar-upload/avatar-upload.module.css rename to web/sdk/react/components/avatar-upload/avatar-upload.module.css diff --git a/web/lib/react/components/avatar-upload/index.tsx b/web/sdk/react/components/avatar-upload/index.tsx similarity index 100% rename from web/lib/react/components/avatar-upload/index.tsx rename to web/sdk/react/components/avatar-upload/index.tsx diff --git a/web/lib/react/components/common/page-header/index.tsx b/web/sdk/react/components/common/page-header/index.tsx similarity index 100% rename from web/lib/react/components/common/page-header/index.tsx rename to web/sdk/react/components/common/page-header/index.tsx diff --git a/web/lib/react/components/common/upcoming-plan-change-banner/index.tsx b/web/sdk/react/components/common/upcoming-plan-change-banner/index.tsx similarity index 100% rename from web/lib/react/components/common/upcoming-plan-change-banner/index.tsx rename to web/sdk/react/components/common/upcoming-plan-change-banner/index.tsx diff --git a/web/lib/react/components/common/upcoming-plan-change-banner/styles.module.css b/web/sdk/react/components/common/upcoming-plan-change-banner/styles.module.css similarity index 100% rename from web/lib/react/components/common/upcoming-plan-change-banner/styles.module.css rename to web/sdk/react/components/common/upcoming-plan-change-banner/styles.module.css diff --git a/web/lib/react/components/container.module.css b/web/sdk/react/components/container.module.css similarity index 100% rename from web/lib/react/components/container.module.css rename to web/sdk/react/components/container.module.css diff --git a/web/lib/react/components/header.module.css b/web/sdk/react/components/header.module.css similarity index 100% rename from web/lib/react/components/header.module.css rename to web/sdk/react/components/header.module.css diff --git a/web/lib/react/components/index.ts b/web/sdk/react/components/index.ts similarity index 100% rename from web/lib/react/components/index.ts rename to web/sdk/react/components/index.ts diff --git a/web/lib/react/components/onboarding/magiclink-verify.tsx b/web/sdk/react/components/onboarding/magiclink-verify.tsx similarity index 100% rename from web/lib/react/components/onboarding/magiclink-verify.tsx rename to web/sdk/react/components/onboarding/magiclink-verify.tsx diff --git a/web/lib/react/components/onboarding/magiclink.tsx b/web/sdk/react/components/onboarding/magiclink.tsx similarity index 100% rename from web/lib/react/components/onboarding/magiclink.tsx rename to web/sdk/react/components/onboarding/magiclink.tsx diff --git a/web/lib/react/components/onboarding/oidc.tsx b/web/sdk/react/components/onboarding/oidc.tsx similarity index 100% rename from web/lib/react/components/onboarding/oidc.tsx rename to web/sdk/react/components/onboarding/oidc.tsx diff --git a/web/lib/react/components/onboarding/onboarding.module.css b/web/sdk/react/components/onboarding/onboarding.module.css similarity index 100% rename from web/lib/react/components/onboarding/onboarding.module.css rename to web/sdk/react/components/onboarding/onboarding.module.css diff --git a/web/lib/react/components/onboarding/signin.tsx b/web/sdk/react/components/onboarding/signin.tsx similarity index 100% rename from web/lib/react/components/onboarding/signin.tsx rename to web/sdk/react/components/onboarding/signin.tsx diff --git a/web/lib/react/components/onboarding/signup.tsx b/web/sdk/react/components/onboarding/signup.tsx similarity index 100% rename from web/lib/react/components/onboarding/signup.tsx rename to web/sdk/react/components/onboarding/signup.tsx diff --git a/web/lib/react/components/onboarding/subscribe.tsx b/web/sdk/react/components/onboarding/subscribe.tsx similarity index 100% rename from web/lib/react/components/onboarding/subscribe.tsx rename to web/sdk/react/components/onboarding/subscribe.tsx diff --git a/web/lib/react/components/onboarding/updates.tsx b/web/sdk/react/components/onboarding/updates.tsx similarity index 100% rename from web/lib/react/components/onboarding/updates.tsx rename to web/sdk/react/components/onboarding/updates.tsx diff --git a/web/lib/react/components/organization/api-keys/add.tsx b/web/sdk/react/components/organization/api-keys/add.tsx similarity index 100% rename from web/lib/react/components/organization/api-keys/add.tsx rename to web/sdk/react/components/organization/api-keys/add.tsx diff --git a/web/lib/react/components/organization/api-keys/columns.tsx b/web/sdk/react/components/organization/api-keys/columns.tsx similarity index 100% rename from web/lib/react/components/organization/api-keys/columns.tsx rename to web/sdk/react/components/organization/api-keys/columns.tsx diff --git a/web/lib/react/components/organization/api-keys/delete.tsx b/web/sdk/react/components/organization/api-keys/delete.tsx similarity index 100% rename from web/lib/react/components/organization/api-keys/delete.tsx rename to web/sdk/react/components/organization/api-keys/delete.tsx diff --git a/web/lib/react/components/organization/api-keys/hooks/useServiceUserTokens.ts b/web/sdk/react/components/organization/api-keys/hooks/useServiceUserTokens.ts similarity index 100% rename from web/lib/react/components/organization/api-keys/hooks/useServiceUserTokens.ts rename to web/sdk/react/components/organization/api-keys/hooks/useServiceUserTokens.ts diff --git a/web/lib/react/components/organization/api-keys/index.tsx b/web/sdk/react/components/organization/api-keys/index.tsx similarity index 100% rename from web/lib/react/components/organization/api-keys/index.tsx rename to web/sdk/react/components/organization/api-keys/index.tsx diff --git a/web/lib/react/components/organization/api-keys/service-user/add-token.tsx b/web/sdk/react/components/organization/api-keys/service-user/add-token.tsx similarity index 100% rename from web/lib/react/components/organization/api-keys/service-user/add-token.tsx rename to web/sdk/react/components/organization/api-keys/service-user/add-token.tsx diff --git a/web/lib/react/components/organization/api-keys/service-user/delete.tsx b/web/sdk/react/components/organization/api-keys/service-user/delete.tsx similarity index 100% rename from web/lib/react/components/organization/api-keys/service-user/delete.tsx rename to web/sdk/react/components/organization/api-keys/service-user/delete.tsx diff --git a/web/lib/react/components/organization/api-keys/service-user/index.tsx b/web/sdk/react/components/organization/api-keys/service-user/index.tsx similarity index 100% rename from web/lib/react/components/organization/api-keys/service-user/index.tsx rename to web/sdk/react/components/organization/api-keys/service-user/index.tsx diff --git a/web/lib/react/components/organization/api-keys/service-user/projects.tsx b/web/sdk/react/components/organization/api-keys/service-user/projects.tsx similarity index 100% rename from web/lib/react/components/organization/api-keys/service-user/projects.tsx rename to web/sdk/react/components/organization/api-keys/service-user/projects.tsx diff --git a/web/lib/react/components/organization/api-keys/service-user/styles.module.css b/web/sdk/react/components/organization/api-keys/service-user/styles.module.css similarity index 100% rename from web/lib/react/components/organization/api-keys/service-user/styles.module.css rename to web/sdk/react/components/organization/api-keys/service-user/styles.module.css diff --git a/web/lib/react/components/organization/api-keys/styles.module.css b/web/sdk/react/components/organization/api-keys/styles.module.css similarity index 100% rename from web/lib/react/components/organization/api-keys/styles.module.css rename to web/sdk/react/components/organization/api-keys/styles.module.css diff --git a/web/lib/react/components/organization/billing/billing.module.css b/web/sdk/react/components/organization/billing/billing.module.css similarity index 100% rename from web/lib/react/components/organization/billing/billing.module.css rename to web/sdk/react/components/organization/billing/billing.module.css diff --git a/web/lib/react/components/organization/billing/cycle-switch/index.tsx b/web/sdk/react/components/organization/billing/cycle-switch/index.tsx similarity index 100% rename from web/lib/react/components/organization/billing/cycle-switch/index.tsx rename to web/sdk/react/components/organization/billing/cycle-switch/index.tsx diff --git a/web/lib/react/components/organization/billing/index.tsx b/web/sdk/react/components/organization/billing/index.tsx similarity index 100% rename from web/lib/react/components/organization/billing/index.tsx rename to web/sdk/react/components/organization/billing/index.tsx diff --git a/web/lib/react/components/organization/billing/invoices/index.tsx b/web/sdk/react/components/organization/billing/invoices/index.tsx similarity index 100% rename from web/lib/react/components/organization/billing/invoices/index.tsx rename to web/sdk/react/components/organization/billing/invoices/index.tsx diff --git a/web/lib/react/components/organization/billing/invoices/invoice.module.css b/web/sdk/react/components/organization/billing/invoices/invoice.module.css similarity index 100% rename from web/lib/react/components/organization/billing/invoices/invoice.module.css rename to web/sdk/react/components/organization/billing/invoices/invoice.module.css diff --git a/web/lib/react/components/organization/billing/payment-issue.tsx b/web/sdk/react/components/organization/billing/payment-issue.tsx similarity index 100% rename from web/lib/react/components/organization/billing/payment-issue.tsx rename to web/sdk/react/components/organization/billing/payment-issue.tsx diff --git a/web/lib/react/components/organization/billing/payment-method.tsx b/web/sdk/react/components/organization/billing/payment-method.tsx similarity index 100% rename from web/lib/react/components/organization/billing/payment-method.tsx rename to web/sdk/react/components/organization/billing/payment-method.tsx diff --git a/web/lib/react/components/organization/billing/upcoming-billing-cycle.tsx b/web/sdk/react/components/organization/billing/upcoming-billing-cycle.tsx similarity index 100% rename from web/lib/react/components/organization/billing/upcoming-billing-cycle.tsx rename to web/sdk/react/components/organization/billing/upcoming-billing-cycle.tsx diff --git a/web/lib/react/components/organization/create.tsx b/web/sdk/react/components/organization/create.tsx similarity index 100% rename from web/lib/react/components/organization/create.tsx rename to web/sdk/react/components/organization/create.tsx diff --git a/web/lib/react/components/organization/domain/add-domain.tsx b/web/sdk/react/components/organization/domain/add-domain.tsx similarity index 100% rename from web/lib/react/components/organization/domain/add-domain.tsx rename to web/sdk/react/components/organization/domain/add-domain.tsx diff --git a/web/lib/react/components/organization/domain/delete.tsx b/web/sdk/react/components/organization/domain/delete.tsx similarity index 100% rename from web/lib/react/components/organization/domain/delete.tsx rename to web/sdk/react/components/organization/domain/delete.tsx diff --git a/web/lib/react/components/organization/domain/domain.columns.tsx b/web/sdk/react/components/organization/domain/domain.columns.tsx similarity index 100% rename from web/lib/react/components/organization/domain/domain.columns.tsx rename to web/sdk/react/components/organization/domain/domain.columns.tsx diff --git a/web/lib/react/components/organization/domain/domain.module.css b/web/sdk/react/components/organization/domain/domain.module.css similarity index 100% rename from web/lib/react/components/organization/domain/domain.module.css rename to web/sdk/react/components/organization/domain/domain.module.css diff --git a/web/lib/react/components/organization/domain/index.tsx b/web/sdk/react/components/organization/domain/index.tsx similarity index 100% rename from web/lib/react/components/organization/domain/index.tsx rename to web/sdk/react/components/organization/domain/index.tsx diff --git a/web/lib/react/components/organization/domain/verify-domain.tsx b/web/sdk/react/components/organization/domain/verify-domain.tsx similarity index 100% rename from web/lib/react/components/organization/domain/verify-domain.tsx rename to web/sdk/react/components/organization/domain/verify-domain.tsx diff --git a/web/lib/react/components/organization/general/delete.tsx b/web/sdk/react/components/organization/general/delete.tsx similarity index 100% rename from web/lib/react/components/organization/general/delete.tsx rename to web/sdk/react/components/organization/general/delete.tsx diff --git a/web/lib/react/components/organization/general/general.module.css b/web/sdk/react/components/organization/general/general.module.css similarity index 100% rename from web/lib/react/components/organization/general/general.module.css rename to web/sdk/react/components/organization/general/general.module.css diff --git a/web/lib/react/components/organization/general/general.workspace.tsx b/web/sdk/react/components/organization/general/general.workspace.tsx similarity index 100% rename from web/lib/react/components/organization/general/general.workspace.tsx rename to web/sdk/react/components/organization/general/general.workspace.tsx diff --git a/web/lib/react/components/organization/general/index.tsx b/web/sdk/react/components/organization/general/index.tsx similarity index 100% rename from web/lib/react/components/organization/general/index.tsx rename to web/sdk/react/components/organization/general/index.tsx diff --git a/web/lib/react/components/organization/members/MemberRemoveConfirm.tsx b/web/sdk/react/components/organization/members/MemberRemoveConfirm.tsx similarity index 100% rename from web/lib/react/components/organization/members/MemberRemoveConfirm.tsx rename to web/sdk/react/components/organization/members/MemberRemoveConfirm.tsx diff --git a/web/lib/react/components/organization/members/index.tsx b/web/sdk/react/components/organization/members/index.tsx similarity index 100% rename from web/lib/react/components/organization/members/index.tsx rename to web/sdk/react/components/organization/members/index.tsx diff --git a/web/lib/react/components/organization/members/invite.tsx b/web/sdk/react/components/organization/members/invite.tsx similarity index 100% rename from web/lib/react/components/organization/members/invite.tsx rename to web/sdk/react/components/organization/members/invite.tsx diff --git a/web/lib/react/components/organization/members/member.columns.tsx b/web/sdk/react/components/organization/members/member.columns.tsx similarity index 100% rename from web/lib/react/components/organization/members/member.columns.tsx rename to web/sdk/react/components/organization/members/member.columns.tsx diff --git a/web/lib/react/components/organization/members/member.types.tsx b/web/sdk/react/components/organization/members/member.types.tsx similarity index 100% rename from web/lib/react/components/organization/members/member.types.tsx rename to web/sdk/react/components/organization/members/member.types.tsx diff --git a/web/lib/react/components/organization/members/members.module.css b/web/sdk/react/components/organization/members/members.module.css similarity index 100% rename from web/lib/react/components/organization/members/members.module.css rename to web/sdk/react/components/organization/members/members.module.css diff --git a/web/lib/react/components/organization/organization.module.css b/web/sdk/react/components/organization/organization.module.css similarity index 100% rename from web/lib/react/components/organization/organization.module.css rename to web/sdk/react/components/organization/organization.module.css diff --git a/web/lib/react/components/organization/plans/confirm-change/index.tsx b/web/sdk/react/components/organization/plans/confirm-change/index.tsx similarity index 100% rename from web/lib/react/components/organization/plans/confirm-change/index.tsx rename to web/sdk/react/components/organization/plans/confirm-change/index.tsx diff --git a/web/lib/react/components/organization/plans/header.tsx b/web/sdk/react/components/organization/plans/header.tsx similarity index 100% rename from web/lib/react/components/organization/plans/header.tsx rename to web/sdk/react/components/organization/plans/header.tsx diff --git a/web/lib/react/components/organization/plans/helpers/helpers.test.ts b/web/sdk/react/components/organization/plans/helpers/helpers.test.ts similarity index 100% rename from web/lib/react/components/organization/plans/helpers/helpers.test.ts rename to web/sdk/react/components/organization/plans/helpers/helpers.test.ts diff --git a/web/lib/react/components/organization/plans/helpers/index.ts b/web/sdk/react/components/organization/plans/helpers/index.ts similarity index 100% rename from web/lib/react/components/organization/plans/helpers/index.ts rename to web/sdk/react/components/organization/plans/helpers/index.ts diff --git a/web/lib/react/components/organization/plans/hooks/usePlans.tsx b/web/sdk/react/components/organization/plans/hooks/usePlans.tsx similarity index 100% rename from web/lib/react/components/organization/plans/hooks/usePlans.tsx rename to web/sdk/react/components/organization/plans/hooks/usePlans.tsx diff --git a/web/lib/react/components/organization/plans/index.tsx b/web/sdk/react/components/organization/plans/index.tsx similarity index 100% rename from web/lib/react/components/organization/plans/index.tsx rename to web/sdk/react/components/organization/plans/index.tsx diff --git a/web/lib/react/components/organization/plans/plans.module.css b/web/sdk/react/components/organization/plans/plans.module.css similarity index 100% rename from web/lib/react/components/organization/plans/plans.module.css rename to web/sdk/react/components/organization/plans/plans.module.css diff --git a/web/lib/react/components/organization/plans/pricing-column.tsx b/web/sdk/react/components/organization/plans/pricing-column.tsx similarity index 100% rename from web/lib/react/components/organization/plans/pricing-column.tsx rename to web/sdk/react/components/organization/plans/pricing-column.tsx diff --git a/web/lib/react/components/organization/preferences/index.tsx b/web/sdk/react/components/organization/preferences/index.tsx similarity index 100% rename from web/lib/react/components/organization/preferences/index.tsx rename to web/sdk/react/components/organization/preferences/index.tsx diff --git a/web/lib/react/components/organization/preferences/preferences.types.ts b/web/sdk/react/components/organization/preferences/preferences.types.ts similarity index 100% rename from web/lib/react/components/organization/preferences/preferences.types.ts rename to web/sdk/react/components/organization/preferences/preferences.types.ts diff --git a/web/lib/react/components/organization/profile.tsx b/web/sdk/react/components/organization/profile.tsx similarity index 100% rename from web/lib/react/components/organization/profile.tsx rename to web/sdk/react/components/organization/profile.tsx diff --git a/web/lib/react/components/organization/project/add.tsx b/web/sdk/react/components/organization/project/add.tsx similarity index 100% rename from web/lib/react/components/organization/project/add.tsx rename to web/sdk/react/components/organization/project/add.tsx diff --git a/web/lib/react/components/organization/project/delete.tsx b/web/sdk/react/components/organization/project/delete.tsx similarity index 100% rename from web/lib/react/components/organization/project/delete.tsx rename to web/sdk/react/components/organization/project/delete.tsx diff --git a/web/lib/react/components/organization/project/general/index.tsx b/web/sdk/react/components/organization/project/general/index.tsx similarity index 100% rename from web/lib/react/components/organization/project/general/index.tsx rename to web/sdk/react/components/organization/project/general/index.tsx diff --git a/web/lib/react/components/organization/project/index.tsx b/web/sdk/react/components/organization/project/index.tsx similarity index 100% rename from web/lib/react/components/organization/project/index.tsx rename to web/sdk/react/components/organization/project/index.tsx diff --git a/web/lib/react/components/organization/project/members/index.tsx b/web/sdk/react/components/organization/project/members/index.tsx similarity index 100% rename from web/lib/react/components/organization/project/members/index.tsx rename to web/sdk/react/components/organization/project/members/index.tsx diff --git a/web/lib/react/components/organization/project/members/member.columns.tsx b/web/sdk/react/components/organization/project/members/member.columns.tsx similarity index 100% rename from web/lib/react/components/organization/project/members/member.columns.tsx rename to web/sdk/react/components/organization/project/members/member.columns.tsx diff --git a/web/lib/react/components/organization/project/members/members.module.css b/web/sdk/react/components/organization/project/members/members.module.css similarity index 100% rename from web/lib/react/components/organization/project/members/members.module.css rename to web/sdk/react/components/organization/project/members/members.module.css diff --git a/web/lib/react/components/organization/project/members/remove.tsx b/web/sdk/react/components/organization/project/members/remove.tsx similarity index 100% rename from web/lib/react/components/organization/project/members/remove.tsx rename to web/sdk/react/components/organization/project/members/remove.tsx diff --git a/web/lib/react/components/organization/project/project.module.css b/web/sdk/react/components/organization/project/project.module.css similarity index 100% rename from web/lib/react/components/organization/project/project.module.css rename to web/sdk/react/components/organization/project/project.module.css diff --git a/web/lib/react/components/organization/project/project.tsx b/web/sdk/react/components/organization/project/project.tsx similarity index 100% rename from web/lib/react/components/organization/project/project.tsx rename to web/sdk/react/components/organization/project/project.tsx diff --git a/web/lib/react/components/organization/project/projects.columns.tsx b/web/sdk/react/components/organization/project/projects.columns.tsx similarity index 100% rename from web/lib/react/components/organization/project/projects.columns.tsx rename to web/sdk/react/components/organization/project/projects.columns.tsx diff --git a/web/lib/react/components/organization/routes.tsx b/web/sdk/react/components/organization/routes.tsx similarity index 100% rename from web/lib/react/components/organization/routes.tsx rename to web/sdk/react/components/organization/routes.tsx diff --git a/web/lib/react/components/organization/security/index.tsx b/web/sdk/react/components/organization/security/index.tsx similarity index 100% rename from web/lib/react/components/organization/security/index.tsx rename to web/sdk/react/components/organization/security/index.tsx diff --git a/web/lib/react/components/organization/security/security.types.tsx b/web/sdk/react/components/organization/security/security.types.tsx similarity index 100% rename from web/lib/react/components/organization/security/security.types.tsx rename to web/sdk/react/components/organization/security/security.types.tsx diff --git a/web/lib/react/components/organization/sessions/index.tsx b/web/sdk/react/components/organization/sessions/index.tsx similarity index 100% rename from web/lib/react/components/organization/sessions/index.tsx rename to web/sdk/react/components/organization/sessions/index.tsx diff --git a/web/lib/react/components/organization/sessions/revoke-session-confirm.tsx b/web/sdk/react/components/organization/sessions/revoke-session-confirm.tsx similarity index 100% rename from web/lib/react/components/organization/sessions/revoke-session-confirm.tsx rename to web/sdk/react/components/organization/sessions/revoke-session-confirm.tsx diff --git a/web/lib/react/components/organization/sessions/revoke-session-final-confirm.tsx b/web/sdk/react/components/organization/sessions/revoke-session-final-confirm.tsx similarity index 100% rename from web/lib/react/components/organization/sessions/revoke-session-final-confirm.tsx rename to web/sdk/react/components/organization/sessions/revoke-session-final-confirm.tsx diff --git a/web/lib/react/components/organization/sessions/session-skeleton.tsx b/web/sdk/react/components/organization/sessions/session-skeleton.tsx similarity index 100% rename from web/lib/react/components/organization/sessions/session-skeleton.tsx rename to web/sdk/react/components/organization/sessions/session-skeleton.tsx diff --git a/web/lib/react/components/organization/sessions/sessions-page.tsx b/web/sdk/react/components/organization/sessions/sessions-page.tsx similarity index 100% rename from web/lib/react/components/organization/sessions/sessions-page.tsx rename to web/sdk/react/components/organization/sessions/sessions-page.tsx diff --git a/web/lib/react/components/organization/sessions/sessions.module.css b/web/sdk/react/components/organization/sessions/sessions.module.css similarity index 100% rename from web/lib/react/components/organization/sessions/sessions.module.css rename to web/sdk/react/components/organization/sessions/sessions.module.css diff --git a/web/lib/react/components/organization/sidebar/helpers.ts b/web/sdk/react/components/organization/sidebar/helpers.ts similarity index 100% rename from web/lib/react/components/organization/sidebar/helpers.ts rename to web/sdk/react/components/organization/sidebar/helpers.ts diff --git a/web/lib/react/components/organization/sidebar/index.tsx b/web/sdk/react/components/organization/sidebar/index.tsx similarity index 100% rename from web/lib/react/components/organization/sidebar/index.tsx rename to web/sdk/react/components/organization/sidebar/index.tsx diff --git a/web/lib/react/components/organization/sidebar/sidebar.module.css b/web/sdk/react/components/organization/sidebar/sidebar.module.css similarity index 100% rename from web/lib/react/components/organization/sidebar/sidebar.module.css rename to web/sdk/react/components/organization/sidebar/sidebar.module.css diff --git a/web/lib/react/components/organization/styles.module.css b/web/sdk/react/components/organization/styles.module.css similarity index 100% rename from web/lib/react/components/organization/styles.module.css rename to web/sdk/react/components/organization/styles.module.css diff --git a/web/lib/react/components/organization/teams/add.tsx b/web/sdk/react/components/organization/teams/add.tsx similarity index 100% rename from web/lib/react/components/organization/teams/add.tsx rename to web/sdk/react/components/organization/teams/add.tsx diff --git a/web/lib/react/components/organization/teams/delete.tsx b/web/sdk/react/components/organization/teams/delete.tsx similarity index 100% rename from web/lib/react/components/organization/teams/delete.tsx rename to web/sdk/react/components/organization/teams/delete.tsx diff --git a/web/lib/react/components/organization/teams/general/index.tsx b/web/sdk/react/components/organization/teams/general/index.tsx similarity index 100% rename from web/lib/react/components/organization/teams/general/index.tsx rename to web/sdk/react/components/organization/teams/general/index.tsx diff --git a/web/lib/react/components/organization/teams/index.tsx b/web/sdk/react/components/organization/teams/index.tsx similarity index 100% rename from web/lib/react/components/organization/teams/index.tsx rename to web/sdk/react/components/organization/teams/index.tsx diff --git a/web/lib/react/components/organization/teams/members/index.tsx b/web/sdk/react/components/organization/teams/members/index.tsx similarity index 100% rename from web/lib/react/components/organization/teams/members/index.tsx rename to web/sdk/react/components/organization/teams/members/index.tsx diff --git a/web/lib/react/components/organization/teams/members/invite.tsx b/web/sdk/react/components/organization/teams/members/invite.tsx similarity index 100% rename from web/lib/react/components/organization/teams/members/invite.tsx rename to web/sdk/react/components/organization/teams/members/invite.tsx diff --git a/web/lib/react/components/organization/teams/members/member.columns.tsx b/web/sdk/react/components/organization/teams/members/member.columns.tsx similarity index 100% rename from web/lib/react/components/organization/teams/members/member.columns.tsx rename to web/sdk/react/components/organization/teams/members/member.columns.tsx diff --git a/web/lib/react/components/organization/teams/members/members.module.css b/web/sdk/react/components/organization/teams/members/members.module.css similarity index 100% rename from web/lib/react/components/organization/teams/members/members.module.css rename to web/sdk/react/components/organization/teams/members/members.module.css diff --git a/web/lib/react/components/organization/teams/team.tsx b/web/sdk/react/components/organization/teams/team.tsx similarity index 100% rename from web/lib/react/components/organization/teams/team.tsx rename to web/sdk/react/components/organization/teams/team.tsx diff --git a/web/lib/react/components/organization/teams/teams.columns.tsx b/web/sdk/react/components/organization/teams/teams.columns.tsx similarity index 100% rename from web/lib/react/components/organization/teams/teams.columns.tsx rename to web/sdk/react/components/organization/teams/teams.columns.tsx diff --git a/web/lib/react/components/organization/teams/teams.module.css b/web/sdk/react/components/organization/teams/teams.module.css similarity index 100% rename from web/lib/react/components/organization/teams/teams.module.css rename to web/sdk/react/components/organization/teams/teams.module.css diff --git a/web/lib/react/components/organization/tokens/add-tokens.tsx b/web/sdk/react/components/organization/tokens/add-tokens.tsx similarity index 100% rename from web/lib/react/components/organization/tokens/add-tokens.tsx rename to web/sdk/react/components/organization/tokens/add-tokens.tsx diff --git a/web/lib/react/components/organization/tokens/index.tsx b/web/sdk/react/components/organization/tokens/index.tsx similarity index 100% rename from web/lib/react/components/organization/tokens/index.tsx rename to web/sdk/react/components/organization/tokens/index.tsx diff --git a/web/lib/react/components/organization/tokens/token.module.css b/web/sdk/react/components/organization/tokens/token.module.css similarity index 100% rename from web/lib/react/components/organization/tokens/token.module.css rename to web/sdk/react/components/organization/tokens/token.module.css diff --git a/web/lib/react/components/organization/tokens/transactions/columns.tsx b/web/sdk/react/components/organization/tokens/transactions/columns.tsx similarity index 100% rename from web/lib/react/components/organization/tokens/transactions/columns.tsx rename to web/sdk/react/components/organization/tokens/transactions/columns.tsx diff --git a/web/lib/react/components/organization/tokens/transactions/index.tsx b/web/sdk/react/components/organization/tokens/transactions/index.tsx similarity index 100% rename from web/lib/react/components/organization/tokens/transactions/index.tsx rename to web/sdk/react/components/organization/tokens/transactions/index.tsx diff --git a/web/lib/react/components/organization/user/index.tsx b/web/sdk/react/components/organization/user/index.tsx similarity index 100% rename from web/lib/react/components/organization/user/index.tsx rename to web/sdk/react/components/organization/user/index.tsx diff --git a/web/lib/react/components/organization/user/update.tsx b/web/sdk/react/components/organization/user/update.tsx similarity index 100% rename from web/lib/react/components/organization/user/update.tsx rename to web/sdk/react/components/organization/user/update.tsx diff --git a/web/lib/react/components/window/index.tsx b/web/sdk/react/components/window/index.tsx similarity index 100% rename from web/lib/react/components/window/index.tsx rename to web/sdk/react/components/window/index.tsx diff --git a/web/lib/react/components/window/window.module.css b/web/sdk/react/components/window/window.module.css similarity index 100% rename from web/lib/react/components/window/window.module.css rename to web/sdk/react/components/window/window.module.css diff --git a/web/lib/react/contexts/CustomizationContext.tsx b/web/sdk/react/contexts/CustomizationContext.tsx similarity index 100% rename from web/lib/react/contexts/CustomizationContext.tsx rename to web/sdk/react/contexts/CustomizationContext.tsx diff --git a/web/lib/react/contexts/FrontierContext.tsx b/web/sdk/react/contexts/FrontierContext.tsx similarity index 100% rename from web/lib/react/contexts/FrontierContext.tsx rename to web/sdk/react/contexts/FrontierContext.tsx diff --git a/web/lib/react/contexts/FrontierProvider.tsx b/web/sdk/react/contexts/FrontierProvider.tsx similarity index 100% rename from web/lib/react/contexts/FrontierProvider.tsx rename to web/sdk/react/contexts/FrontierProvider.tsx diff --git a/web/lib/react/contexts/useMaxAllowedInstancesGuard.tsx b/web/sdk/react/contexts/useMaxAllowedInstancesGuard.tsx similarity index 100% rename from web/lib/react/contexts/useMaxAllowedInstancesGuard.tsx rename to web/sdk/react/contexts/useMaxAllowedInstancesGuard.tsx diff --git a/web/lib/react/hooks/useBillingPermission.ts b/web/sdk/react/hooks/useBillingPermission.ts similarity index 100% rename from web/lib/react/hooks/useBillingPermission.ts rename to web/sdk/react/hooks/useBillingPermission.ts diff --git a/web/lib/react/hooks/useConnectQueryPolling.tsx b/web/sdk/react/hooks/useConnectQueryPolling.tsx similarity index 100% rename from web/lib/react/hooks/useConnectQueryPolling.tsx rename to web/sdk/react/hooks/useConnectQueryPolling.tsx diff --git a/web/lib/react/hooks/useCopyToClipboard.ts b/web/sdk/react/hooks/useCopyToClipboard.ts similarity index 100% rename from web/lib/react/hooks/useCopyToClipboard.ts rename to web/sdk/react/hooks/useCopyToClipboard.ts diff --git a/web/lib/react/hooks/useLastActiveTracker.ts b/web/sdk/react/hooks/useLastActiveTracker.ts similarity index 100% rename from web/lib/react/hooks/useLastActiveTracker.ts rename to web/sdk/react/hooks/useLastActiveTracker.ts diff --git a/web/lib/react/hooks/useMessages.ts b/web/sdk/react/hooks/useMessages.ts similarity index 100% rename from web/lib/react/hooks/useMessages.ts rename to web/sdk/react/hooks/useMessages.ts diff --git a/web/lib/react/hooks/useOrganizationDomain.ts b/web/sdk/react/hooks/useOrganizationDomain.ts similarity index 100% rename from web/lib/react/hooks/useOrganizationDomain.ts rename to web/sdk/react/hooks/useOrganizationDomain.ts diff --git a/web/lib/react/hooks/useOrganizationDomains.ts b/web/sdk/react/hooks/useOrganizationDomains.ts similarity index 100% rename from web/lib/react/hooks/useOrganizationDomains.ts rename to web/sdk/react/hooks/useOrganizationDomains.ts diff --git a/web/lib/react/hooks/useOrganizationMembers.ts b/web/sdk/react/hooks/useOrganizationMembers.ts similarity index 100% rename from web/lib/react/hooks/useOrganizationMembers.ts rename to web/sdk/react/hooks/useOrganizationMembers.ts diff --git a/web/lib/react/hooks/useOrganizationProjects.ts b/web/sdk/react/hooks/useOrganizationProjects.ts similarity index 100% rename from web/lib/react/hooks/useOrganizationProjects.ts rename to web/sdk/react/hooks/useOrganizationProjects.ts diff --git a/web/lib/react/hooks/useOrganizationTeams.ts b/web/sdk/react/hooks/useOrganizationTeams.ts similarity index 100% rename from web/lib/react/hooks/useOrganizationTeams.ts rename to web/sdk/react/hooks/useOrganizationTeams.ts diff --git a/web/lib/react/hooks/usePermissions.ts b/web/sdk/react/hooks/usePermissions.ts similarity index 100% rename from web/lib/react/hooks/usePermissions.ts rename to web/sdk/react/hooks/usePermissions.ts diff --git a/web/lib/react/hooks/usePreferences.ts b/web/sdk/react/hooks/usePreferences.ts similarity index 100% rename from web/lib/react/hooks/usePreferences.ts rename to web/sdk/react/hooks/usePreferences.ts diff --git a/web/lib/react/hooks/useSessions.ts b/web/sdk/react/hooks/useSessions.ts similarity index 100% rename from web/lib/react/hooks/useSessions.ts rename to web/sdk/react/hooks/useSessions.ts diff --git a/web/lib/react/hooks/useTerminology.ts b/web/sdk/react/hooks/useTerminology.ts similarity index 100% rename from web/lib/react/hooks/useTerminology.ts rename to web/sdk/react/hooks/useTerminology.ts diff --git a/web/lib/react/hooks/useTokens.ts b/web/sdk/react/hooks/useTokens.ts similarity index 100% rename from web/lib/react/hooks/useTokens.ts rename to web/sdk/react/hooks/useTokens.ts diff --git a/web/lib/react/index.ts b/web/sdk/react/index.ts similarity index 100% rename from web/lib/react/index.ts rename to web/sdk/react/index.ts diff --git a/web/lib/react/package.json b/web/sdk/react/package.json similarity index 100% rename from web/lib/react/package.json rename to web/sdk/react/package.json diff --git a/web/lib/react/utils/constants.ts b/web/sdk/react/utils/constants.ts similarity index 100% rename from web/lib/react/utils/constants.ts rename to web/sdk/react/utils/constants.ts diff --git a/web/lib/react/utils/fetch.ts b/web/sdk/react/utils/fetch.ts similarity index 100% rename from web/lib/react/utils/fetch.ts rename to web/sdk/react/utils/fetch.ts diff --git a/web/lib/react/utils/index.ts b/web/sdk/react/utils/index.ts similarity index 100% rename from web/lib/react/utils/index.ts rename to web/sdk/react/utils/index.ts diff --git a/web/lib/scripts/bump-version.js b/web/sdk/scripts/bump-version.js similarity index 100% rename from web/lib/scripts/bump-version.js rename to web/sdk/scripts/bump-version.js diff --git a/web/lib/shared/types.ts b/web/sdk/shared/types.ts similarity index 100% rename from web/lib/shared/types.ts rename to web/sdk/shared/types.ts diff --git a/web/lib/shared/utils/time.ts b/web/sdk/shared/utils/time.ts similarity index 100% rename from web/lib/shared/utils/time.ts rename to web/sdk/shared/utils/time.ts diff --git a/web/lib/src/index.ts b/web/sdk/src/index.ts similarity index 100% rename from web/lib/src/index.ts rename to web/sdk/src/index.ts diff --git a/web/lib/src/types.ts b/web/sdk/src/types.ts similarity index 100% rename from web/lib/src/types.ts rename to web/sdk/src/types.ts diff --git a/web/lib/tsconfig.json b/web/sdk/tsconfig.json similarity index 100% rename from web/lib/tsconfig.json rename to web/sdk/tsconfig.json diff --git a/web/lib/tsup.config.ts b/web/sdk/tsup.config.ts similarity index 100% rename from web/lib/tsup.config.ts rename to web/sdk/tsup.config.ts diff --git a/web/lib/utils/index.ts b/web/sdk/utils/index.ts similarity index 100% rename from web/lib/utils/index.ts rename to web/sdk/utils/index.ts diff --git a/web/lib/utils/timestamp.ts b/web/sdk/utils/timestamp.ts similarity index 100% rename from web/lib/utils/timestamp.ts rename to web/sdk/utils/timestamp.ts