diff --git a/messages/en-US.json b/messages/en-US.json
index ffd28a518..58a772967 100644
--- a/messages/en-US.json
+++ b/messages/en-US.json
@@ -173,6 +173,10 @@
"resourcePoliciesDefaultBadgeText": "Default policy",
"resourcePoliciesCreate": "Create Resource Policy",
"resourcePoliciesCreateDescription": "Follow the steps below to create a new policy",
+ "resourcePolicyName": "Policy Name",
+ "resourcePolicyNameDescription": "Give this policy a name to identify it across your resources",
+ "resourcePolicyNamePlaceholder": "e.g. Internal Access Policy",
+ "policiesSeeAll": "See All Policies",
"authentication": "Authentication",
"protected": "Protected",
"notProtected": "Not Protected",
diff --git a/src/app/[orgId]/settings/(private)/resources/policies/create/page.tsx b/src/app/[orgId]/settings/(private)/resources/policies/create/page.tsx
index 02afa06cd..e43ef39ee 100644
--- a/src/app/[orgId]/settings/(private)/resources/policies/create/page.tsx
+++ b/src/app/[orgId]/settings/(private)/resources/policies/create/page.tsx
@@ -1,7 +1,11 @@
+import { CreatePolicyForm } from "@app/components/CreatePolicyForm";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
+import { Button } from "@app/components/ui/button";
import { getCachedOrg } from "@app/lib/api/getCachedOrg";
+import OrgProvider from "@app/providers/OrgProvider";
import type { GetOrgResponse } from "@server/routers/org";
import { getTranslations } from "next-intl/server";
+import Link from "next/link";
import { redirect } from "next/navigation";
export interface CreateResourcePolicyPageProps {
@@ -23,10 +27,22 @@ export default async function CreateResourcePolicyPage(
}
return (
<>
-
+
+
+
+
+
+
+
+
+
>
);
}
diff --git a/src/components/CreatePolicyForm.tsx b/src/components/CreatePolicyForm.tsx
new file mode 100644
index 000000000..b1cb49a01
--- /dev/null
+++ b/src/components/CreatePolicyForm.tsx
@@ -0,0 +1,620 @@
+"use client";
+
+import {
+ SettingsContainer,
+ SettingsSection,
+ SettingsSectionBody,
+ SettingsSectionDescription,
+ SettingsSectionFooter,
+ SettingsSectionForm,
+ SettingsSectionHeader,
+ SettingsSectionTitle
+} from "@app/components/Settings";
+import { SwitchInput } from "@app/components/SwitchInput";
+import { Tag, TagInput } from "@app/components/tags/tag-input";
+import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
+import { Button } from "@app/components/ui/button";
+import {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage
+} from "@app/components/ui/form";
+import { Input } from "@app/components/ui/input";
+import { InfoPopup } from "@app/components/ui/info-popup";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue
+} from "@app/components/ui/select";
+import { useEnvContext } from "@app/hooks/useEnvContext";
+import { useOrgContext } from "@app/hooks/useOrgContext";
+import { usePaidStatus } from "@app/hooks/usePaidStatus";
+import { createApiClient } from "@app/lib/api";
+import { getUserDisplayName } from "@app/lib/getUserDisplayName";
+import { orgQueries } from "@app/lib/queries";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { build } from "@server/build";
+import { tierMatrix } from "@server/lib/billing/tierMatrix";
+import { UserType } from "@server/types/UserTypes";
+import { useQuery } from "@tanstack/react-query";
+import { Binary, Bot, InfoIcon, Key } from "lucide-react";
+import { useTranslations } from "next-intl";
+import { useRouter } from "next/navigation";
+import { useActionState, useMemo, useState } from "react";
+import { useForm } from "react-hook-form";
+import z from "zod";
+
+const createPolicySchema = z.object({
+ name: z.string().min(1).max(255),
+ sso: z.boolean().default(true),
+ skipToIdpId: z.number().nullable().optional(),
+ emailWhitelistEnabled: z.boolean().default(false),
+ roles: z.array(
+ z.object({
+ id: z.string(),
+ text: z.string()
+ })
+ ),
+ users: z.array(
+ z.object({
+ id: z.string(),
+ text: z.string()
+ })
+ ),
+ emails: z.array(
+ z.object({
+ id: z.string(),
+ text: z.string()
+ })
+ )
+});
+
+export type CreatePolicyFormProps = {};
+
+export function CreatePolicyForm({}: CreatePolicyFormProps) {
+ const { org } = useOrgContext();
+ const t = useTranslations();
+ const { env } = useEnvContext();
+ const api = createApiClient({ env });
+ const [, formAction, isSubmitting] = useActionState(onSubmit, null);
+ const router = useRouter();
+ const { isPaidUser } = usePaidStatus();
+
+ const { data: orgRoles = [], isLoading: isLoadingOrgRoles } = useQuery(
+ orgQueries.roles({
+ orgId: org.org.orgId
+ })
+ );
+ const { data: orgUsers = [], isLoading: isLoadingOrgUsers } = useQuery(
+ orgQueries.users({
+ orgId: org.org.orgId
+ })
+ );
+ const { data: orgIdps = [], isLoading: isLoadingOrgIdps } = useQuery(
+ orgQueries.identityProviders({
+ orgId: org.org.orgId,
+ useOrgOnlyIdp: env.app.identityProviderMode === "org"
+ })
+ );
+
+ const form = useForm({
+ resolver: zodResolver(createPolicySchema),
+ defaultValues: {
+ name: "",
+ sso: true,
+ skipToIdpId: null,
+ emailWhitelistEnabled: false,
+ roles: [],
+ users: [],
+ emails: []
+ }
+ });
+
+ const [ssoEnabled, setSsoEnabled] = useState(true);
+ const [whitelistEnabled, setWhitelistEnabled] = useState(false);
+ const [selectedIdpId, setSelectedIdpId] = useState(null);
+ const [activeRolesTagIndex, setActiveRolesTagIndex] = useState<
+ number | null
+ >(null);
+ const [activeUsersTagIndex, setActiveUsersTagIndex] = useState<
+ number | null
+ >(null);
+ const [activeEmailTagIndex, setActiveEmailTagIndex] = useState<
+ number | null
+ >(null);
+
+ async function onSubmit() {
+ // ...
+ }
+
+ const allRoles = useMemo(() => {
+ return orgRoles
+ .map((role) => ({
+ id: role.roleId.toString(),
+ text: role.name
+ }))
+ .filter((role) => role.text !== "Admin");
+ }, [orgRoles]);
+
+ const allUsers = useMemo(() => {
+ return orgUsers.map((user) => ({
+ id: user.id.toString(),
+ text: `${getUserDisplayName({
+ email: user.email,
+ username: user.username
+ })}${user.type !== UserType.Internal ? ` (${user.idpName})` : ""}`
+ }));
+ }, [orgUsers]);
+
+ const allIdps = useMemo(() => {
+ if (build === "saas") {
+ if (isPaidUser(tierMatrix.orgOidc)) {
+ return orgIdps.map((idp) => ({
+ id: idp.idpId,
+ text: idp.name
+ }));
+ }
+ } else {
+ return orgIdps.map((idp) => ({
+ id: idp.idpId,
+ text: idp.name
+ }));
+ }
+ return [];
+ }, [orgIdps]);
+
+ const pageLoading =
+ isLoadingOrgRoles || isLoadingOrgUsers || isLoadingOrgIdps;
+
+ if (pageLoading) {
+ return <>>;
+ }
+
+ return (
+
+
+ );
+}