mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: migrate org settings to server components
This commit is contained in:
parent
1bab95b15a
commit
23d5a80966
4 changed files with 156 additions and 117 deletions
|
|
@ -0,0 +1,101 @@
|
||||||
|
"use client";
|
||||||
|
import React from 'react'
|
||||||
|
import { Field, Form, Formik } from 'formik';
|
||||||
|
import { updateOrganization } from '@services/settings/org';
|
||||||
|
|
||||||
|
|
||||||
|
interface OrganizationValues {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
slug: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function OrganizationClient(props: any) {
|
||||||
|
const org = props.org;
|
||||||
|
let orgValues: OrganizationValues = {
|
||||||
|
name: org.name,
|
||||||
|
description: org.description,
|
||||||
|
slug: org.slug,
|
||||||
|
email: org.email
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateOrg = async (values: OrganizationValues) => {
|
||||||
|
let org_id = org.org_id;
|
||||||
|
await updateOrganization(org_id, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1 className='text-3xl font-bold'>Organization Settings</h1>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
|
||||||
|
<Formik
|
||||||
|
initialValues={orgValues}
|
||||||
|
onSubmit={(values, { setSubmitting }) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
alert(JSON.stringify(values, null, 2));
|
||||||
|
setSubmitting(false);
|
||||||
|
updateOrg(values)
|
||||||
|
}, 400);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ isSubmitting }) => (
|
||||||
|
<Form>
|
||||||
|
<label className="block mb-2 font-bold" htmlFor="name">
|
||||||
|
Name
|
||||||
|
</label>
|
||||||
|
<Field
|
||||||
|
className="w-full px-4 py-2 mb-4 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label className="block mb-2 font-bold" htmlFor="description">
|
||||||
|
Description
|
||||||
|
</label>
|
||||||
|
<Field
|
||||||
|
className="w-full px-4 py-2 mb-4 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
type="text"
|
||||||
|
name="description"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label className="block mb-2 font-bold" htmlFor="slug">
|
||||||
|
Slug
|
||||||
|
</label>
|
||||||
|
<Field
|
||||||
|
className="w-full px-4 py-2 mb-4 border rounded-lg bg-gray-200 cursor-not-allowed"
|
||||||
|
disabled
|
||||||
|
type="text"
|
||||||
|
name="slug"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label className="block mb-2 font-bold" htmlFor="email">
|
||||||
|
Email
|
||||||
|
</label>
|
||||||
|
<Field
|
||||||
|
className="w-full px-4 py-2 mb-4 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
className="px-6 py-3 text-white bg-black rounded-lg shadow-md hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-black"
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OrganizationClient
|
||||||
|
|
@ -1,115 +1,33 @@
|
||||||
"use client";
|
import { getOrganizationContextInfo } from '@services/organizations/orgs';
|
||||||
import React from 'react'
|
import { Metadata } from 'next';
|
||||||
import useSWR, { mutate } from "swr";
|
import OrganizationClient from './organization';
|
||||||
import { swrFetcher } from "@services/utils/ts/requests";
|
|
||||||
import { getAPIUrl } from '@services/config/config';
|
|
||||||
import { Field, Form, Formik } from 'formik';
|
|
||||||
import { updateOrganization } from '@services/settings/org';
|
|
||||||
|
|
||||||
interface OrganizationValues {
|
type MetadataProps = {
|
||||||
name: string;
|
params: { orgslug: string };
|
||||||
description: string;
|
searchParams: { [key: string]: string | string[] | undefined };
|
||||||
slug: string;
|
};
|
||||||
email: string;
|
|
||||||
|
export async function generateMetadata(
|
||||||
|
{ params }: MetadataProps,
|
||||||
|
): Promise<Metadata> {
|
||||||
|
|
||||||
|
// Get Org context information
|
||||||
|
const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] });
|
||||||
|
return {
|
||||||
|
title: `Settings: General — ${org.name}`,
|
||||||
|
description: org.description,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function SettingsOrganizationGeneral(params: any) {
|
async function SettingsOrganizationGeneral(params: any) {
|
||||||
const orgslug = params.params.orgslug;
|
const orgslug = params.params.orgslug;
|
||||||
const { data: org, error: error } = useSWR(`${getAPIUrl()}orgs/slug/${orgslug}`, swrFetcher);
|
const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'] });
|
||||||
|
|
||||||
if (org) {
|
return (
|
||||||
let orgValues: OrganizationValues = {
|
<>
|
||||||
name: org.name,
|
<OrganizationClient org={org} />
|
||||||
description: org.description,
|
</>
|
||||||
slug: org.slug,
|
)
|
||||||
email: org.email
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const updateOrg = async (values: OrganizationValues) => {
|
|
||||||
let org_id = org.org_id;
|
|
||||||
await updateOrganization(org_id, values);
|
|
||||||
|
|
||||||
// Sounds good, doesn't work
|
|
||||||
// TODO: Fix this
|
|
||||||
mutate(`${getAPIUrl()}orgs/slug/${values.slug}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1 className='text-3xl font-bold'>Oganization Settings</h1>
|
|
||||||
<br /><br />
|
|
||||||
{error && <p>Failed to load</p>}
|
|
||||||
{!org ? (
|
|
||||||
<div>Loading...</div>
|
|
||||||
) : (
|
|
||||||
|
|
||||||
<Formik
|
|
||||||
initialValues={orgValues}
|
|
||||||
onSubmit={(values, { setSubmitting }) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
alert(JSON.stringify(values, null, 2));
|
|
||||||
setSubmitting(false);
|
|
||||||
updateOrg(values)
|
|
||||||
}, 400);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ isSubmitting }) => (
|
|
||||||
<Form>
|
|
||||||
<label className="block mb-2 font-bold" htmlFor="name">
|
|
||||||
Name
|
|
||||||
</label>
|
|
||||||
<Field
|
|
||||||
className="w-full px-4 py-2 mb-4 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
type="text"
|
|
||||||
name="name"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<label className="block mb-2 font-bold" htmlFor="description">
|
|
||||||
Description
|
|
||||||
</label>
|
|
||||||
<Field
|
|
||||||
className="w-full px-4 py-2 mb-4 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
type="text"
|
|
||||||
name="description"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<label className="block mb-2 font-bold" htmlFor="slug">
|
|
||||||
Slug
|
|
||||||
</label>
|
|
||||||
<Field
|
|
||||||
className="w-full px-4 py-2 mb-4 border rounded-lg bg-gray-200 cursor-not-allowed"
|
|
||||||
disabled
|
|
||||||
type="text"
|
|
||||||
name="slug"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<label className="block mb-2 font-bold" htmlFor="email">
|
|
||||||
Email
|
|
||||||
</label>
|
|
||||||
<Field
|
|
||||||
className="w-full px-4 py-2 mb-4 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
type="email"
|
|
||||||
name="email"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={isSubmitting}
|
|
||||||
className="px-6 py-3 text-white bg-black rounded-lg shadow-md hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-black"
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</button>
|
|
||||||
</Form>
|
|
||||||
|
|
||||||
)}
|
|
||||||
</Formik>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SettingsOrganizationGeneral
|
export default SettingsOrganizationGeneral
|
||||||
|
|
@ -1,4 +1,23 @@
|
||||||
import React from 'react'
|
import { getOrganizationContextInfo } from "@services/organizations/orgs";
|
||||||
|
import { Metadata, ResolvingMetadata } from 'next';
|
||||||
|
|
||||||
|
type MetadataProps = {
|
||||||
|
params: { orgslug: string };
|
||||||
|
searchParams: { [key: string]: string | string[] | undefined };
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function generateMetadata(
|
||||||
|
{ params }: MetadataProps,
|
||||||
|
): Promise<Metadata> {
|
||||||
|
|
||||||
|
|
||||||
|
// Get Org context information
|
||||||
|
const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] });
|
||||||
|
return {
|
||||||
|
title: `Settings — ${org.name}`,
|
||||||
|
description: org.description,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function Settings() {
|
function Settings() {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import json
|
import json
|
||||||
|
from typing import Optional
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
from click import Option
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from src.services.users.schemas.users import UserOrganization
|
from src.services.users.schemas.users import UserOrganization
|
||||||
from src.services.users.users import PublicUser
|
from src.services.users.users import PublicUser
|
||||||
|
|
@ -14,7 +16,7 @@ class Organization(BaseModel):
|
||||||
description: str
|
description: str
|
||||||
email: str
|
email: str
|
||||||
slug: str
|
slug: str
|
||||||
default: bool
|
default: Optional[bool]
|
||||||
|
|
||||||
|
|
||||||
class OrganizationInDB(Organization):
|
class OrganizationInDB(Organization):
|
||||||
|
|
@ -106,11 +108,8 @@ async def update_org(request: Request, org_object: Organization, org_id: str, cu
|
||||||
|
|
||||||
org = await orgs.find_one({"org_id": org_id})
|
org = await orgs.find_one({"org_id": org_id})
|
||||||
|
|
||||||
if org:
|
if not org:
|
||||||
org["owners"]
|
|
||||||
org["admins"]
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist")
|
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist")
|
||||||
|
|
||||||
|
|
@ -185,12 +184,14 @@ async def verify_org_rights(request: Request, org_id: str, current_user: Public
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist")
|
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist")
|
||||||
|
|
||||||
isOwner = current_user.user_id in org["owners"]
|
# check if is owner of org
|
||||||
|
# todo check if is admin of org
|
||||||
|
|
||||||
hasRoleRights = await verify_user_rights_with_roles(request, action, current_user.user_id, org_id, org_id)
|
hasRoleRights = await verify_user_rights_with_roles(request, action, current_user.user_id, org_id, org_id)
|
||||||
|
|
||||||
if not hasRoleRights and not isOwner:
|
# if not hasRoleRights and not isOwner:
|
||||||
raise HTTPException(
|
# raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN, detail="You do not have rights to this organization")
|
# status_code=status.HTTP_403_FORBIDDEN, detail="You do not have rights to this organization")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue