mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
fix: some fix responsive
This commit is contained in:
@@ -34,17 +34,10 @@ export default async function RoutePage(props: PageParams<{ slug: string }>) {
|
|||||||
const storageChannels = await getOrganizationStorageChannels(organization.id)
|
const storageChannels = await getOrganizationStorageChannels(organization.id)
|
||||||
const permissions = computeOrganizationPermissions(activeMember);
|
const permissions = computeOrganizationPermissions(activeMember);
|
||||||
|
|
||||||
// Fetch users for the form
|
|
||||||
const users = await db.query.user.findMany({
|
const users = await db.query.user.findMany({
|
||||||
where: (fields) => isNull(fields.deletedAt)
|
where: (fields) => isNull(fields.deletedAt)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Re-fetch organization with members to match type expectations if needed,
|
|
||||||
// although getOrganization returns OrganizationWithMembers usually, let's verify or cast.
|
|
||||||
// getOrganization returns Organization type from database schema, which includes relations if defined in auth lib.
|
|
||||||
// However, OrganizationForm expects OrganizationWithMembers.
|
|
||||||
// Let's ensure we have members.
|
|
||||||
|
|
||||||
const organizationWithMembers = await db.query.organization.findFirst({
|
const organizationWithMembers = await db.query.organization.findFirst({
|
||||||
where: eq(drizzleDb.schemas.organization.id, organization.id),
|
where: eq(drizzleDb.schemas.organization.id, organization.id),
|
||||||
with: {
|
with: {
|
||||||
@@ -58,7 +51,6 @@ export default async function RoutePage(props: PageParams<{ slug: string }>) {
|
|||||||
|
|
||||||
if (!organizationWithMembers) notFound();
|
if (!organizationWithMembers) notFound();
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
@@ -73,11 +65,7 @@ export default async function RoutePage(props: PageParams<{ slug: string }>) {
|
|||||||
organization={organizationWithMembers}
|
organization={organizationWithMembers}
|
||||||
users={users}
|
users={users}
|
||||||
currentUser={user}
|
currentUser={user}
|
||||||
>
|
/>
|
||||||
<Button variant="outline">
|
|
||||||
<GearIcon className="w-7 h-7"/>
|
|
||||||
</Button>
|
|
||||||
</EditOrganizationDialog>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {Setting} from "@/db/schema/01_setting";
|
|||||||
import {SettingsEmailSection} from "@/components/wrappers/dashboard/admin/settings/email/settings-email-section";
|
import {SettingsEmailSection} from "@/components/wrappers/dashboard/admin/settings/email/settings-email-section";
|
||||||
import {SettingsStorageSection} from "@/components/wrappers/dashboard/admin/settings/storage/settings-storage-section";
|
import {SettingsStorageSection} from "@/components/wrappers/dashboard/admin/settings/storage/settings-storage-section";
|
||||||
import {StorageChannelWith} from "@/db/schema/12_storage-channel";
|
import {StorageChannelWith} from "@/db/schema/12_storage-channel";
|
||||||
|
import {MailboxIcon, Save} from "lucide-react";
|
||||||
|
|
||||||
export type SettingsTabsProps = {
|
export type SettingsTabsProps = {
|
||||||
settings: Setting
|
settings: Setting
|
||||||
@@ -30,29 +31,44 @@ export const SettingsTabs = ({settings, storageChannels}: SettingsTabsProps) =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
name: 'System Email',
|
||||||
|
value: 'email',
|
||||||
|
icon: MailboxIcon,
|
||||||
|
content: (
|
||||||
|
<SettingsEmailSection settings={settings}/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Default Storage',
|
||||||
|
value: 'storage',
|
||||||
|
icon: Save,
|
||||||
|
content: (
|
||||||
|
<SettingsStorageSection storageChannels={storageChannels} settings={settings}/>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full mt-3">
|
<div className="h-full mt-3">
|
||||||
<Tabs className="h-full" value={tab} onValueChange={handleChangeTab}>
|
<Tabs className="h-full gap-4" value={tab} onValueChange={handleChangeTab}>
|
||||||
<TabsList className='bg-background rounded-none border-b p-0 min-w-48'>
|
<TabsList>
|
||||||
<TabsTrigger
|
{tabs.map(({icon: Icon, name, value}) => (
|
||||||
value="email"
|
<TabsTrigger key={value} value={value} className='flex items-center gap-1 px-2.5 sm:px-3'>
|
||||||
className='bg-background data-[state=active]:border-primary dark:data-[state=active]:border-primary h-full rounded-none border-0 border-b-2 border-transparent data-[state=active]:shadow-none'
|
<Icon/>
|
||||||
>
|
{name}
|
||||||
Email
|
</TabsTrigger>
|
||||||
</TabsTrigger>
|
))}
|
||||||
<TabsTrigger
|
|
||||||
value="storage"
|
|
||||||
className='bg-background data-[state=active]:border-primary dark:data-[state=active]:border-primary h-full rounded-none border-0 border-b-2 border-transparent data-[state=active]:shadow-none'
|
|
||||||
>
|
|
||||||
Default storage
|
|
||||||
</TabsTrigger>
|
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent className="h-full" value="email">
|
|
||||||
<SettingsEmailSection settings={settings}/>
|
{tabs.map(tab => (
|
||||||
</TabsContent>
|
<TabsContent key={tab.value} value={tab.value} className="h-full">
|
||||||
<TabsContent className="h-full" value="storage">
|
{tab.content}
|
||||||
<SettingsStorageSection storageChannels={storageChannels} settings={settings}/>
|
</TabsContent>
|
||||||
</TabsContent>
|
))}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -64,15 +64,15 @@ export const SettingsStorageSection = ({settings, storageChannels}: SettingsStor
|
|||||||
await mutation.mutateAsync(values);
|
await mutation.mutateAsync(values);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="storageChannelId"
|
name="storageChannelId"
|
||||||
render={({field}) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex items-center justify-center">
|
<FormItem className="flex-grow min-w-[200px] sm:flex-grow-0 sm:w-64">
|
||||||
<Select value={field.value} onValueChange={field.onChange}>
|
<Select value={field.value} onValueChange={field.onChange}>
|
||||||
<SelectTrigger className="w-90 h-full mb-0">
|
<SelectTrigger className="w-full h-full mb-0">
|
||||||
<SelectValue placeholder="Select a default storage channel"/>
|
<SelectValue placeholder="Select a default storage channel" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{storageChannels.map((channel) => (
|
{storageChannels.map((channel) => (
|
||||||
@@ -80,10 +80,9 @@ export const SettingsStorageSection = ({settings, storageChannels}: SettingsStor
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{getChannelIcon(channel.provider)}
|
{getChannelIcon(channel.provider)}
|
||||||
<span className="font-medium">{channel.name}</span>
|
<span className="font-medium">{channel.name}</span>
|
||||||
<span
|
<span className="text-[9px] uppercase bg-secondary px-1.5 py-0.5 rounded">
|
||||||
className="text-[9px] uppercase bg-secondary px-1.5 py-0.5 rounded">
|
{channel.provider}
|
||||||
{channel.provider}
|
</span>
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
@@ -92,10 +91,11 @@ export const SettingsStorageSection = ({settings, storageChannels}: SettingsStor
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<ButtonWithLoading type="submit">
|
<ButtonWithLoading className="flex-shrink-0 w-full sm:w-auto" type="submit">
|
||||||
Confirm
|
Confirm
|
||||||
</ButtonWithLoading>
|
</ButtonWithLoading>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,38 +8,46 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import {OrganizationForm} from "@/features/organization/components/organization.form";
|
import {OrganizationForm} from "@/features/organization/components/organization.form";
|
||||||
import {useState} from "react";
|
import {ReactNode, useState} from "react";
|
||||||
import {Button, buttonVariants} from "@/components/ui/button";
|
import {Button, buttonVariants} from "@/components/ui/button";
|
||||||
import {GearIcon} from "@radix-ui/react-icons";
|
import {GearIcon} from "@radix-ui/react-icons";
|
||||||
import {OrganizationWithMembers} from "@/db/schema/03_organization";
|
import {OrganizationWithMembers} from "@/db/schema/03_organization";
|
||||||
import {User} from "@/db/schema/02_user";
|
import {User} from "@/db/schema/02_user";
|
||||||
import {User as BetterAuthUser} from "better-auth";
|
import {User as BetterAuthUser} from "better-auth";
|
||||||
|
import {useRouter} from "next/navigation";
|
||||||
|
|
||||||
type EditOrganizationDialogProps = {
|
type EditOrganizationDialogProps = {
|
||||||
children?: React.ReactNode;
|
|
||||||
organization: OrganizationWithMembers;
|
organization: OrganizationWithMembers;
|
||||||
users: User[];
|
users: User[];
|
||||||
currentUser: BetterAuthUser;
|
currentUser: BetterAuthUser;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EditOrganizationDialog = ({children, organization, users, currentUser}: EditOrganizationDialogProps) => {
|
export const EditOrganizationDialog = ({
|
||||||
|
organization,
|
||||||
|
users,
|
||||||
|
currentUser,
|
||||||
|
}: EditOrganizationDialogProps) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
{children ? children : (
|
<div className={buttonVariants({variant: "outline", className: "cursor-pointer"})}>
|
||||||
<Button variant="outline">
|
<GearIcon className="w-7 h-7"/>
|
||||||
<GearIcon className="w-4 h-4 mr-2"/> Edit
|
</div>
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent>
|
<DialogContent
|
||||||
|
onOpenAutoFocus={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Edit {organization.name}</DialogTitle>
|
<DialogTitle>Edit {organization.name}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<OrganizationForm
|
<OrganizationForm
|
||||||
onSuccess={() => setOpen(false)}
|
onSuccess={() => {
|
||||||
|
setOpen(false)
|
||||||
|
router.refresh()
|
||||||
|
}}
|
||||||
defaultValues={organization}
|
defaultValues={organization}
|
||||||
users={users}
|
users={users}
|
||||||
currentUser={currentUser}
|
currentUser={currentUser}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import {GearIcon} from "@radix-ui/react-icons";
|
|||||||
import {EmptyStatePlaceholder} from "@/components/wrappers/common/empty-state-placeholder";
|
import {EmptyStatePlaceholder} from "@/components/wrappers/common/empty-state-placeholder";
|
||||||
|
|
||||||
type ProjectDialogProps = {
|
type ProjectDialogProps = {
|
||||||
children?: ReactNode;
|
|
||||||
databases: DatabaseWith[];
|
databases: DatabaseWith[];
|
||||||
organization: Organization;
|
organization: Organization;
|
||||||
project?: ProjectWith;
|
project?: ProjectWith;
|
||||||
|
|||||||
Reference in New Issue
Block a user