mirror of
https://github.com/colanode/colanode.git
synced 2025-12-29 00:25:03 +01:00
Improve table views
This commit is contained in:
@@ -14,23 +14,19 @@ import {
|
||||
TooltipTrigger,
|
||||
} from '@/renderer/components/ui/tooltip';
|
||||
import { FieldDeleteDialog } from '@/renderer/components/databases/fields/field-delete-dialog';
|
||||
import { useMutation } from '@/renderer/hooks/use-mutation';
|
||||
import { ViewDeleteDialog } from '@/renderer/components/databases/view-delete-dialog';
|
||||
import { SmartTextInput } from '@/renderer/components/ui/smart-text-input';
|
||||
import { useWorkspace } from '@/renderer/contexts/workspace';
|
||||
import { AvatarPopover } from '@/renderer/components/avatars/avatar-popover';
|
||||
import { Button } from '@/renderer/components/ui/button';
|
||||
import { Avatar } from '@/renderer/components/avatars/avatar';
|
||||
import { Eye, EyeOff, Settings, Trash2 } from 'lucide-react';
|
||||
import { FieldIcon } from '@/renderer/components/databases/fields/field-icon';
|
||||
import { ViewIcon } from '../view-icon';
|
||||
|
||||
export const TableViewSettingsPopover = () => {
|
||||
const workspace = useWorkspace();
|
||||
const database = useDatabase();
|
||||
const view = useView();
|
||||
|
||||
const { mutate, isPending } = useMutation();
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [openDelete, setOpenDelete] = React.useState(false);
|
||||
const [deleteFieldId, setDeleteFieldId] = React.useState<string | null>(null);
|
||||
@@ -48,36 +44,27 @@ export const TableViewSettingsPopover = () => {
|
||||
{database.canEdit ? (
|
||||
<AvatarPopover
|
||||
onPick={(avatar) => {
|
||||
if (isPending) return;
|
||||
if (avatar === view.avatar) return;
|
||||
|
||||
mutate({
|
||||
input: {
|
||||
type: 'node_attribute_set',
|
||||
nodeId: view.id,
|
||||
path: 'avatar',
|
||||
value: avatar,
|
||||
userId: workspace.userId,
|
||||
},
|
||||
});
|
||||
view.updateAvatar(avatar);
|
||||
}}
|
||||
>
|
||||
<Button type="button" variant="outline" size="icon">
|
||||
<Avatar
|
||||
<ViewIcon
|
||||
id={view.id}
|
||||
name={view.name}
|
||||
avatar={view.avatar}
|
||||
className="h-6 w-6"
|
||||
type={view.type}
|
||||
className="size-4"
|
||||
/>
|
||||
</Button>
|
||||
</AvatarPopover>
|
||||
) : (
|
||||
<Button type="button" variant="outline" size="icon">
|
||||
<Avatar
|
||||
<ViewIcon
|
||||
id={view.id}
|
||||
name={view.name}
|
||||
avatar={view.avatar}
|
||||
className="h-6 w-6"
|
||||
type={view.type}
|
||||
className="size-4"
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
@@ -85,18 +72,9 @@ export const TableViewSettingsPopover = () => {
|
||||
value={view.name}
|
||||
readOnly={!database.canEdit}
|
||||
onChange={(newName) => {
|
||||
if (isPending) return;
|
||||
if (newName === view.name) return;
|
||||
|
||||
mutate({
|
||||
input: {
|
||||
type: 'node_attribute_set',
|
||||
nodeId: view.id,
|
||||
path: 'name',
|
||||
value: newName,
|
||||
userId: workspace.userId,
|
||||
},
|
||||
});
|
||||
view.rename(newName);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
36
apps/desktop/src/renderer/components/databases/view-icon.tsx
Normal file
36
apps/desktop/src/renderer/components/databases/view-icon.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Avatar } from '@/renderer/components/avatars/avatar';
|
||||
import { Calendar, Database, SquareKanban, Table } from 'lucide-react';
|
||||
|
||||
interface ViewIconProps {
|
||||
id: string;
|
||||
name: string;
|
||||
avatar: string | null;
|
||||
type: 'table' | 'board' | 'calendar';
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ViewIcon = ({
|
||||
id,
|
||||
name,
|
||||
avatar,
|
||||
type,
|
||||
className,
|
||||
}: ViewIconProps) => {
|
||||
if (avatar) {
|
||||
return <Avatar id={id} name={name} avatar={avatar} className={className} />;
|
||||
}
|
||||
|
||||
if (type === 'table') {
|
||||
return <Table className={className} />;
|
||||
}
|
||||
|
||||
if (type === 'calendar') {
|
||||
return <Calendar className={className} />;
|
||||
}
|
||||
|
||||
if (type === 'board') {
|
||||
return <SquareKanban className={className} />;
|
||||
}
|
||||
|
||||
return <Database className={className} />;
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { cn } from '@/shared/lib/utils';
|
||||
import { Avatar } from '@/renderer/components/avatars/avatar';
|
||||
import { ViewAttributes } from '@colanode/core';
|
||||
import { ViewIcon } from '@/renderer/components/databases/view-icon';
|
||||
|
||||
interface ViewTabProps {
|
||||
view: ViewAttributes;
|
||||
@@ -19,7 +19,13 @@ export const ViewTab = ({ view, isActive, onClick }: ViewTabProps) => {
|
||||
onClick={() => onClick()}
|
||||
onKeyDown={() => onClick()}
|
||||
>
|
||||
<Avatar id={view.id} name={view.name} avatar={view.avatar} size="small" />
|
||||
<ViewIcon
|
||||
id={view.id}
|
||||
name={view.name}
|
||||
avatar={view.avatar}
|
||||
type={view.type}
|
||||
className="size-4"
|
||||
/>
|
||||
{view.name}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -61,6 +61,7 @@ export const View = ({ view }: ViewProps) => {
|
||||
id: view.id,
|
||||
name: view.name,
|
||||
avatar: view.avatar,
|
||||
type: view.type,
|
||||
fields,
|
||||
filters: Object.values(view.filters),
|
||||
sorts: Object.values(view.sorts),
|
||||
@@ -68,6 +69,40 @@ export const View = ({ view }: ViewProps) => {
|
||||
nameWidth: view.nameWidth ?? getDefaultNameWidth(),
|
||||
isSearchBarOpened,
|
||||
isSortsOpened,
|
||||
rename: (name: string) => {
|
||||
if (!database.canEdit) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewCopy = { ...view };
|
||||
viewCopy.name = name;
|
||||
|
||||
mutate({
|
||||
input: {
|
||||
type: 'view_update',
|
||||
userId: workspace.userId,
|
||||
databaseId: database.id,
|
||||
view: viewCopy,
|
||||
},
|
||||
});
|
||||
},
|
||||
updateAvatar: (avatar: string) => {
|
||||
if (!database.canEdit) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewCopy = { ...view };
|
||||
viewCopy.avatar = avatar;
|
||||
|
||||
mutate({
|
||||
input: {
|
||||
type: 'view_update',
|
||||
userId: workspace.userId,
|
||||
databaseId: database.id,
|
||||
view: viewCopy,
|
||||
},
|
||||
});
|
||||
},
|
||||
setFieldDisplay: (id: string, display: boolean) => {
|
||||
if (!database.canEdit) {
|
||||
return;
|
||||
|
||||
@@ -34,7 +34,7 @@ interface RecordContext {
|
||||
getCollaboratorValue: (field: CollaboratorFieldAttributes) => string[] | null;
|
||||
getDateValue: (field: DateFieldAttributes) => Date | null;
|
||||
getEmailValue: (field: EmailFieldAttributes) => string | null;
|
||||
getFileValue: (field: FileFieldAttributes) => string | null;
|
||||
getFileValue: (field: FileFieldAttributes) => string[] | null;
|
||||
getMultiSelectValue: (field: MultiSelectFieldAttributes) => string[];
|
||||
getNumberValue: (field: NumberFieldAttributes) => number | null;
|
||||
getPhoneValue: (field: PhoneFieldAttributes) => string | null;
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
SortDirection,
|
||||
ViewFilterAttributes,
|
||||
ViewSortAttributes,
|
||||
ViewType,
|
||||
} from '@colanode/core';
|
||||
import { ViewField } from '@/shared/types/databases';
|
||||
import { createContext, useContext } from 'react';
|
||||
@@ -10,6 +11,7 @@ interface ViewContext {
|
||||
id: string;
|
||||
name: string;
|
||||
avatar: string | null;
|
||||
type: ViewType;
|
||||
fields: ViewField[];
|
||||
filters: ViewFilterAttributes[];
|
||||
sorts: ViewSortAttributes[];
|
||||
@@ -17,6 +19,8 @@ interface ViewContext {
|
||||
nameWidth: number;
|
||||
isSearchBarOpened: boolean;
|
||||
isSortsOpened: boolean;
|
||||
rename: (name: string) => void;
|
||||
updateAvatar: (avatar: string) => void;
|
||||
setFieldDisplay: (id: string, display: boolean) => void;
|
||||
resizeField: (id: string, width: number) => void;
|
||||
resizeName: (width: number) => void;
|
||||
|
||||
@@ -78,6 +78,7 @@ export const databaseAttributesSchema = z.object({
|
||||
});
|
||||
|
||||
export type DatabaseAttributes = z.infer<typeof databaseAttributesSchema>;
|
||||
export type ViewType = 'table' | 'board' | 'calendar';
|
||||
|
||||
export const databaseModel: NodeModel = {
|
||||
type: 'database',
|
||||
|
||||
Reference in New Issue
Block a user