Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { toast } from 'sonner';
import {
CollaboratorFieldAttributes,
DatabaseViewFilterAttributes,
FieldValue,
} from '@brainbox/core';
import { Avatar } from '@brainbox/ui/components/avatars/avatar';
import { BoardViewColumn } from '@brainbox/ui/components/databases/boards/board-view-column';
Expand Down Expand Up @@ -103,28 +102,13 @@ export const BoardViewColumnsCollaborator = ({
return;
}

let newValue: FieldValue = value;
const currentValue = record.fields[field.id];
if (currentValue && currentValue.type === 'string_array') {
const newOptions = [
...currentValue.value.filter(
(collaboratorId) =>
collaboratorId !== collaborator.value
),
...newValue.value,
];

newValue = {
type: 'string_array',
value: newOptions,
};
}

// For board view, REPLACE the value entirely (move to new column)
// Don't merge - dropping on a column means "assign to this collaborator"
const result = await window.brainbox.executeMutation({
type: 'record.field.value.set',
recordId: record.id,
fieldId: field.id,
value: newValue,
value: value,
accountId: workspace.accountId,
workspaceId: workspace.id,
});
Expand Down Expand Up @@ -162,31 +146,17 @@ export const BoardViewColumnsCollaborator = ({
return;
}

if (!value) {
const result = await window.brainbox.executeMutation({
type: 'record.field.value.delete',
recordId: record.id,
fieldId: field.id,
accountId: workspace.accountId,
workspaceId: workspace.id,
});

if (!result.success) {
toast.error(result.error.message);
}
} else {
const result = await window.brainbox.executeMutation({
type: 'record.field.value.set',
recordId: record.id,
fieldId: field.id,
value,
accountId: workspace.accountId,
workspaceId: workspace.id,
});

if (!result.success) {
toast.error(result.error.message);
}
// "No Value" column always clears the field
const result = await window.brainbox.executeMutation({
type: 'record.field.value.delete',
recordId: record.id,
fieldId: field.id,
accountId: workspace.accountId,
workspaceId: workspace.id,
});

if (!result.success) {
toast.error(result.error.message);
}
},
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { toast } from 'sonner';

import {
DatabaseViewFilterAttributes,
FieldValue,
MultiSelectFieldAttributes,
SelectOptionAttributes,
} from '@brainbox/core';
Expand Down Expand Up @@ -113,27 +112,13 @@ export const BoardViewColumnsMultiSelect = ({
return;
}

let newValue: FieldValue = value;
const currentValue = record.fields[field.id];
if (currentValue && currentValue.type === 'string_array') {
const newOptions = [
...currentValue.value.filter(
(optionId) => optionId !== option.id
),
...newValue.value,
];

newValue = {
type: 'string_array',
value: newOptions,
};
}

// For board view, REPLACE the value entirely (move to new column)
// Don't merge - dropping on a column means "set to this option"
const result = await window.brainbox.executeMutation({
type: 'record.field.value.set',
recordId: record.id,
fieldId: field.id,
value: newValue,
value: value,
accountId: workspace.accountId,
workspaceId: workspace.id,
});
Expand Down Expand Up @@ -172,31 +157,17 @@ export const BoardViewColumnsMultiSelect = ({
return;
}

if (!value) {
const result = await window.brainbox.executeMutation({
type: 'record.field.value.delete',
recordId: record.id,
fieldId: field.id,
accountId: workspace.accountId,
workspaceId: workspace.id,
});

if (!result.success) {
toast.error(result.error.message);
}
} else {
const result = await window.brainbox.executeMutation({
type: 'record.field.value.set',
recordId: record.id,
fieldId: field.id,
value,
accountId: workspace.accountId,
workspaceId: workspace.id,
});

if (!result.success) {
toast.error(result.error.message);
}
// "No Value" column always clears the field
const result = await window.brainbox.executeMutation({
type: 'record.field.value.delete',
recordId: record.id,
fieldId: field.id,
accountId: workspace.accountId,
workspaceId: workspace.id,
});

if (!result.success) {
toast.error(result.error.message);
}
},
}}
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/databases/boards/board-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const BoardViewContent = () => {
<Fragment>
<div className="flex flex-row justify-between border-b">
<ViewTabs />
<div className="invisible flex flex-row items-center justify-end group-hover/database:visible">
<div className="flex flex-row items-center justify-end gap-0.5 text-muted-foreground">
<ViewFullscreenButton />
<BoardViewSettings />
<ViewSortButton />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const CalendarView = () => {
<Fragment>
<div className="flex flex-row justify-between border-b border-border/50 bg-background/95 backdrop-blur-sm">
<ViewTabs />
<div className="flex flex-row items-center justify-end gap-0.5 opacity-0 transition-opacity duration-150 ease-out group-hover/database:opacity-100">
<div className="flex flex-row items-center justify-end gap-0.5 text-muted-foreground">
{groupByField && <CalendarViewNoValueCount field={groupByField} />}
<ViewFullscreenButton />
<CalendarViewSettings />
Expand Down
32 changes: 27 additions & 5 deletions packages/ui/src/components/databases/database-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod/v4';

import { Avatar } from '@brainbox/ui/components/avatars/avatar';
import { AvatarPopover } from '@brainbox/ui/components/avatars/avatar-popover';
import { Button } from '@brainbox/ui/components/ui/button';
import {
Form,
Expand Down Expand Up @@ -31,7 +33,7 @@ interface DatabaseFormProps {
}

export const DatabaseForm = ({
id: _id,
id,
values,
isPending,
submitText,
Expand All @@ -44,6 +46,8 @@ export const DatabaseForm = ({
defaultValues: values,
});

const avatarValue = form.watch('avatar');

useEffect(() => {
if (readOnly) return;

Expand All @@ -54,23 +58,41 @@ export const DatabaseForm = ({
return () => clearTimeout(timeoutId);
}, [readOnly]);

const iconButton = (
<Button type="button" variant="outline" size="icon" disabled={readOnly}>
{avatarValue ? (
<Avatar id={id} avatar={avatarValue} size="small" />
) : (
<Database className="h-4 w-4 text-muted-foreground" />
)}
</Button>
);

return (
<Form {...form}>
<form
className="flex flex-col"
onSubmit={form.handleSubmit(handleSubmit)}
>
<div className="flex-grow flex flex-row items-end gap-2 py-2 pb-4">
<Button type="button" variant="outline" size="icon" disabled>
<Database className="h-4 w-4 text-muted-foreground" />
</Button>
{readOnly ? (
iconButton
) : (
<AvatarPopover onPick={(avatar) => form.setValue('avatar', avatar)}>
{iconButton}
</AvatarPopover>
)}
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem className="flex-1">
<FormControl>
<Input readOnly={readOnly} placeholder="Name" {...field} />
<Input
readOnly={readOnly}
placeholder="Database name"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { useDragLayer } from 'react-dnd';

import { ViewField } from '@brainbox/client/types';
import { FieldIcon } from '@brainbox/ui/components/databases/fields/field-icon';
import { dragPreviewStyles } from '@brainbox/ui/lib/drag-feedback';
import { cn } from '@brainbox/ui/lib/utils';

interface TableColumnDragLayerProps {
Expand Down Expand Up @@ -73,18 +72,20 @@ export const TableColumnDragLayer: React.FC<TableColumnDragLayerProps> = ({
<div
style={{
transform: `translate(-50%, -50%)`,
width: `${item.width}px`,
width: `${Math.min(item.width, 200)}px`,
}}
className={cn(
dragPreviewStyles,
'flex h-8 items-center gap-2 p-2 opacity-90'
'flex h-8 items-center gap-2 px-3',
'bg-background border border-primary/50 rounded-md',
'shadow-lg shadow-primary/10',
'ring-2 ring-primary/20'
)}
>
<FieldIcon
type={item.field.type}
className="size-4 pointer-events-none text-fg-muted"
className="size-4 pointer-events-none text-primary shrink-0"
/>
<p className="flex-1 pointer-events-none select-none text-sm text-fg-default">
<p className="flex-1 pointer-events-none select-none text-sm font-medium truncate">
{item.field.name}
</p>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,36 @@
import { Plus, Table } from 'lucide-react';

import { Button } from '@brainbox/ui/components/ui/button';
import { useDatabase } from '@brainbox/ui/contexts/database';
import { useDatabaseView } from '@brainbox/ui/contexts/database-view';

export const TableViewEmptyPlaceholder = () => {
const database = useDatabase();
const view = useDatabaseView();
const hasFilters = view.filters.length > 0;

return (
<div className="flex w-full flex-col items-center justify-center border-b p-10 text-sm text-muted-foreground">
<p>No records</p>
<div className="flex w-full flex-col items-center justify-center border-b py-16 px-6 text-center">
<div className="flex items-center justify-center size-12 rounded-full bg-surface-muted mb-4">
<Table className="size-6 text-muted-foreground" />
</div>
<h3 className="text-base font-medium text-foreground">No records yet</h3>
<p className="mt-1.5 text-sm text-muted-foreground max-w-sm">
{hasFilters
? 'No records match your current filters. Try adjusting or clearing filters to see more records.'
: 'Add your first record to start organizing your data in this database.'}
</p>
{database.canCreateRecord && (
<Button
variant="outline"
size="sm"
className="mt-4"
onClick={() => view.createRecord()}
>
<Plus className="size-4 mr-1.5" />
Add record
</Button>
)}
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,8 @@ export const TableViewFieldHeader = memo(
width: `${viewField.width}px`,
height: '2rem',
}}
minWidth={100}
maxWidth={500}
minWidth={50}
maxWidth={1200}
size={{
width: isResizing ? `${resizeWidth}px` : `${viewField.width}px`,
height: '2rem',
Expand Down Expand Up @@ -380,6 +380,16 @@ export const TableViewFieldHeader = memo(
}
}}
>
{/* Double-click target for auto-fit */}
{database.canEdit && (
<div
className="absolute right-0 top-0 bottom-0 w-2 cursor-col-resize z-20"
onDoubleClick={(e) => {
e.stopPropagation();
view.autoFitFieldWidth(viewField.field.id);
}}
/>
)}
<Popover
modal={true}
open={openPopover}
Expand Down Expand Up @@ -484,8 +494,9 @@ export const TableViewFieldHeader = memo(
<span
aria-hidden
className={cn(
'absolute top-0 bottom-0 w-[2px] bg-primary shadow-[0_0_0_1px_theme(colors.primary/60%)]',
hoverEdge === 'left' ? 'left-0' : 'right-0'
'absolute top-0 bottom-0 w-1 bg-primary rounded-full',
'shadow-[0_0_8px_2px_theme(colors.primary/40%)]',
hoverEdge === 'left' ? '-left-0.5' : '-right-0.5'
)}
style={{ pointerEvents: 'none' }}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ export const TableViewNameHeader = () => {
width: `${view.nameWidth}px`,
height: '2rem',
}}
minWidth={300}
maxWidth={500}
minWidth={120}
maxWidth={800}
size={{
width: isResizing ? `${resizeWidth}px` : `${view.nameWidth}px`,
height: '2rem',
Expand Down
Loading