mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: init org settings
This commit is contained in:
parent
838a94eec1
commit
55c097a50e
6 changed files with 227 additions and 16 deletions
|
|
@ -142,7 +142,7 @@ async def update_org(
|
||||||
|
|
||||||
slug_available = result.first()
|
slug_available = result.first()
|
||||||
|
|
||||||
if slug_available:
|
if slug_available and slug_available.id != org_id:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT,
|
status_code=status.HTTP_409_CONFLICT,
|
||||||
detail="Organization slug already exists",
|
detail="Organization slug already exists",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
'use client';
|
||||||
|
import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
|
||||||
|
import { getUriWithOrg } from '@services/config/config'
|
||||||
|
import { Info } from 'lucide-react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import React from 'react'
|
||||||
|
import { motion } from 'framer-motion'
|
||||||
|
import OrgEditGeneral from '@components/Dashboard/Org/OrgEditGeneral/OrgEditGeneral'
|
||||||
|
|
||||||
|
export type OrgParams = {
|
||||||
|
subpage: string
|
||||||
|
orgslug: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function OrgPage({ params }: { params: OrgParams }) {
|
||||||
|
return (
|
||||||
|
<div className='h-full w-full bg-[#f8f8f8]'>
|
||||||
|
<div className='pl-10 pr-10 tracking-tight bg-[#fcfbfc] shadow-[0px_4px_16px_rgba(0,0,0,0.02)]'>
|
||||||
|
<BreadCrumbs type='org' ></BreadCrumbs>
|
||||||
|
<div className='my-2 tracking-tighter'>
|
||||||
|
<div className='w-100 flex justify-between'>
|
||||||
|
<div className='pt-3 flex font-bold text-4xl'>Organization Settings</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='flex space-x-5 font-black text-sm'>
|
||||||
|
<Link href={getUriWithOrg(params.orgslug, "") + `/dash/org/settings/general`}>
|
||||||
|
<div className={`py-2 w-fit text-center border-black transition-all ease-linear ${params.subpage.toString() === 'general' ? 'border-b-4' : 'opacity-50'} cursor-pointer`}>
|
||||||
|
|
||||||
|
<div className='flex items-center space-x-2.5 mx-2'>
|
||||||
|
<Info size={16} />
|
||||||
|
<div>General</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='h-6'></div>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
transition={{ duration: 0.10, type: "spring", stiffness: 80 }}
|
||||||
|
>
|
||||||
|
{params.subpage == 'general' ? <OrgEditGeneral /> : ''}
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OrgPage
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
"use client";
|
||||||
|
import React, { use, useEffect, useState } from 'react'
|
||||||
|
import { Field, Form, Formik } from 'formik';
|
||||||
|
import { updateOrganization, uploadOrganizationLogo } from '@services/settings/org';
|
||||||
|
import { UploadCloud } from 'lucide-react';
|
||||||
|
import { revalidateTags } from '@services/utils/ts/requests';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useOrg } from '@components/Contexts/OrgContext';
|
||||||
|
|
||||||
|
interface OrganizationValues {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
slug: string;
|
||||||
|
logo: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function OrgEditGeneral(props: any) {
|
||||||
|
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||||
|
const router = useRouter();
|
||||||
|
const org = useOrg() as any;
|
||||||
|
// ...
|
||||||
|
|
||||||
|
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (event.target.files && event.target.files.length > 0) {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
setSelectedFile(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadLogo = async () => {
|
||||||
|
if (selectedFile) {
|
||||||
|
let org_id = org.id;
|
||||||
|
await uploadOrganizationLogo(org_id, selectedFile);
|
||||||
|
setSelectedFile(null); // Reset the selected file
|
||||||
|
await revalidateTags(['organizations'], org.slug);
|
||||||
|
router.refresh();
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let orgValues: OrganizationValues = {
|
||||||
|
name: org?.name,
|
||||||
|
description: org?.description,
|
||||||
|
slug: org?.slug,
|
||||||
|
logo: org?.logo,
|
||||||
|
email: org?.email
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateOrg = async (values: OrganizationValues) => {
|
||||||
|
let org_id = org.id;
|
||||||
|
await updateOrganization(org_id, values);
|
||||||
|
|
||||||
|
// Mutate the org
|
||||||
|
await revalidateTags(['organizations'], org.slug);
|
||||||
|
router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
}
|
||||||
|
, [org])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='ml-10 mr-10 mx-auto bg-white rounded-xl shadow-sm px-6 py-5'>
|
||||||
|
<Formik
|
||||||
|
enableReinitialize
|
||||||
|
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">
|
||||||
|
Logo
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-center w-full ">
|
||||||
|
<input
|
||||||
|
className="w-full px-4 py-2 mr-1 border rounded-lg bg-gray-200 cursor-not-allowed"
|
||||||
|
type="file"
|
||||||
|
name="logo"
|
||||||
|
onChange={handleFileChange}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={uploadLogo}
|
||||||
|
disabled={isSubmitting || selectedFile === null}
|
||||||
|
className="px-6 py-3 text-white bg-gray-500 rounded-lg hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-black"
|
||||||
|
>
|
||||||
|
<UploadCloud size={24} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<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 OrgEditGeneral
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { useCourse } from '@components/Contexts/CourseContext'
|
import { useCourse } from '@components/Contexts/CourseContext'
|
||||||
import { Book, ChevronRight, User } from 'lucide-react'
|
import { Book, ChevronRight, School, User } from 'lucide-react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React, { use, useEffect } from 'react'
|
import React, { use, useEffect } from 'react'
|
||||||
|
|
||||||
type BreadCrumbsProps = {
|
type BreadCrumbsProps = {
|
||||||
type: 'courses' | 'user' | 'users',
|
type: 'courses' | 'user' | 'users' | 'org'
|
||||||
last_breadcrumb?: string
|
last_breadcrumb?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ function BreadCrumbs(props: BreadCrumbsProps) {
|
||||||
<div className='flex items-center space-x-1'>
|
<div className='flex items-center space-x-1'>
|
||||||
{props.type == 'courses' ? <div className='flex space-x-2 items-center'> <Book className='text-gray' size={14}></Book><Link href='/dash/courses'>Courses</Link></div> : ''}
|
{props.type == 'courses' ? <div className='flex space-x-2 items-center'> <Book className='text-gray' size={14}></Book><Link href='/dash/courses'>Courses</Link></div> : ''}
|
||||||
{props.type == 'user' ? <div className='flex space-x-2 items-center'> <User className='text-gray' size={14}></User><Link href='/dash/user/settings/general'>Account Settings</Link></div> : ''}
|
{props.type == 'user' ? <div className='flex space-x-2 items-center'> <User className='text-gray' size={14}></User><Link href='/dash/user/settings/general'>Account Settings</Link></div> : ''}
|
||||||
|
{props.type == 'org' ? <div className='flex space-x-2 items-center'> <School className='text-gray' size={14}></School><Link href='/dash/users'>Organization Settings</Link></div> : ''}
|
||||||
<div className='flex items-center space-x-1 first-letter:uppercase'>
|
<div className='flex items-center space-x-1 first-letter:uppercase'>
|
||||||
{props.last_breadcrumb ? <ChevronRight size={17} /> : ''}
|
{props.last_breadcrumb ? <ChevronRight size={17} /> : ''}
|
||||||
<div className='first-letter:uppercase'> {props.last_breadcrumb}</div>
|
<div className='first-letter:uppercase'> {props.last_breadcrumb}</div>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { useAuth } from '@components/Security/AuthContext';
|
||||||
import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
|
import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
|
||||||
import LearnHouseDashboardLogo from '@public/dashLogo.png';
|
import LearnHouseDashboardLogo from '@public/dashLogo.png';
|
||||||
import Avvvatars from 'avvvatars-react';
|
import Avvvatars from 'avvvatars-react';
|
||||||
import { ArrowLeft, Book, Home, Settings } from 'lucide-react'
|
import { ArrowLeft, Book, Home, School, Settings } from 'lucide-react'
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React, { use, useEffect } from 'react'
|
import React, { use, useEffect } from 'react'
|
||||||
|
|
@ -32,23 +32,31 @@ function LeftMenu() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{ background: "linear-gradient(0deg, rgba(0, 0, 0, 0.20) 0%, rgba(0, 0, 0, 0.20) 100%), radial-gradient(271.56% 105.16% at 50% -5.16%, rgba(255, 255, 255, 0.18) 0%, rgba(0, 0, 0, 0.00) 100%), #2E2D2D" }}
|
style={{ background: "linear-gradient(0deg, rgba(0, 0, 0, 0.20) 0%, rgba(0, 0, 0, 0.20) 100%), radial-gradient(271.56% 105.16% at 50% -5.16%, rgba(255, 255, 255, 0.18) 0%, rgba(0, 0, 0, 0.00) 100%), #2E2D2D" }}
|
||||||
className='flex flex-col w-24 bg-black h-screen text-white shadow-xl'>
|
className='flex flex-col w-28 bg-black h-screen text-white shadow-xl'>
|
||||||
<div className='flex flex-col h-full'>
|
<div className='flex flex-col h-full'>
|
||||||
<div className='flex h-20 mt-6'>
|
<div className='flex h-20 mt-6'>
|
||||||
<Link className='flex flex-col items-center mx-auto space-y-1' href={"/dash"}>
|
<Link className='flex flex-col items-center mx-auto space-y-3' href={"/"}>
|
||||||
|
<ToolTip content={'Back to Home'} slateBlack sideOffset={8} side='right' >
|
||||||
<Image alt="Learnhouse logo" width={40} src={LearnHouseDashboardLogo} />
|
<Image alt="Learnhouse logo" width={40} src={LearnHouseDashboardLogo} />
|
||||||
|
</ToolTip>
|
||||||
|
<ToolTip content={'Your Organization'} slateBlack sideOffset={8} side='right' >
|
||||||
|
<div className='py-1 px-3 bg-black/40 opacity-40 rounded-md text-[10px] justify-center text-center'>{org?.name}</div>
|
||||||
|
</ToolTip>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex grow flex-col justify-center space-y-5 items-center mx-auto'>
|
<div className='flex grow flex-col justify-center space-y-5 items-center mx-auto'>
|
||||||
<ToolTip content={"Back to " + org?.name + "'s Home"} slateBlack sideOffset={8} side='right' >
|
{/* <ToolTip content={"Back to " + org?.name + "'s Home"} slateBlack sideOffset={8} side='right' >
|
||||||
<Link className='bg-white text-black hover:text-white rounded-lg p-2 hover:bg-white/10 transition-all ease-linear' href={`/`} ><ArrowLeft className='hover:text-white' size={18} /></Link>
|
<Link className='bg-white text-black hover:text-white rounded-lg p-2 hover:bg-white/10 transition-all ease-linear' href={`/`} ><ArrowLeft className='hover:text-white' size={18} /></Link>
|
||||||
</ToolTip>
|
</ToolTip> */}
|
||||||
<ToolTip content={"Home"} slateBlack sideOffset={8} side='right' >
|
<ToolTip content={"Home"} slateBlack sideOffset={8} side='right' >
|
||||||
<Link className='bg-white/5 rounded-lg p-2 hover:bg-white/10 transition-all ease-linear' href={`/dash`} ><Home size={18} /></Link>
|
<Link className='bg-white/5 rounded-lg p-2 hover:bg-white/10 transition-all ease-linear' href={`/dash`} ><Home size={18} /></Link>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
<ToolTip content={"Courses"} slateBlack sideOffset={8} side='right' >
|
<ToolTip content={"Courses"} slateBlack sideOffset={8} side='right' >
|
||||||
<Link className='bg-white/5 rounded-lg p-2 hover:bg-white/10 transition-all ease-linear' href={`/dash/courses`} ><Book size={18} /></Link>
|
<Link className='bg-white/5 rounded-lg p-2 hover:bg-white/10 transition-all ease-linear' href={`/dash/courses`} ><Book size={18} /></Link>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
<ToolTip content={"Organization"} slateBlack sideOffset={8} side='right' >
|
||||||
|
<Link className='bg-white/5 rounded-lg p-2 hover:bg-white/10 transition-all ease-linear' href={`/dash/org/settings/general`} ><School size={18} /></Link>
|
||||||
|
</ToolTip>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-col mx-auto pb-7 space-y-2'>
|
<div className='flex flex-col mx-auto pb-7 space-y-2'>
|
||||||
<ToolTip content={auth.user.username + "'s Settings"} slateBlack sideOffset={8} side='right' >
|
<ToolTip content={auth.user.username + "'s Settings"} slateBlack sideOffset={8} side='right' >
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ import { HeaderProfileBox } from "@components/Security/HeaderProfileBox";
|
||||||
import MenuLinks from "./MenuLinks";
|
import MenuLinks from "./MenuLinks";
|
||||||
import { getOrgLogoMediaDirectory } from "@services/media/media";
|
import { getOrgLogoMediaDirectory } from "@services/media/media";
|
||||||
import { MessageSquareIcon } from "lucide-react";
|
import { MessageSquareIcon } from "lucide-react";
|
||||||
import { Tooltip } from "@radix-ui/react-tooltip";
|
|
||||||
import ToolTip from "@components/StyledElements/Tooltip/Tooltip";
|
|
||||||
import Modal from "@components/StyledElements/Modal/Modal";
|
import Modal from "@components/StyledElements/Modal/Modal";
|
||||||
import FeedbackModal from "../Modals/Feedback/Feedback";
|
import FeedbackModal from "../Modals/Feedback/Feedback";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
@ -39,9 +37,9 @@ export const Menu = (props: any) => {
|
||||||
<div className="logo flex ">
|
<div className="logo flex ">
|
||||||
<Link href={getUriWithOrg(orgslug, "/")}>
|
<Link href={getUriWithOrg(orgslug, "/")}>
|
||||||
<div className="flex w-auto h-9 rounded-md items-center m-auto py-1 justify-center" >
|
<div className="flex w-auto h-9 rounded-md items-center m-auto py-1 justify-center" >
|
||||||
{org?.logo ? (
|
{org?.logo_image ? (
|
||||||
<img
|
<img
|
||||||
src={`${getOrgLogoMediaDirectory(org.org_id, org?.logo)}`}
|
src={`${getOrgLogoMediaDirectory(org.org_uuid, org?.logo_image)}`}
|
||||||
alt="Learnhouse"
|
alt="Learnhouse"
|
||||||
style={{ width: "auto", height: "100%" }}
|
style={{ width: "auto", height: "100%" }}
|
||||||
className="rounded-md"
|
className="rounded-md"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue