Compare commits

..

3 Commits

Author SHA1 Message Date
c6fc2f6026 fix: file upload btn 2025-09-15 23:31:03 +00:00
8f82f8d5ad feat: tweak file table col sizes 2025-09-15 23:27:56 +00:00
9d62de2c99 feat: make file icon in table smaller 2025-09-15 23:04:38 +00:00
4 changed files with 71 additions and 58 deletions

View File

@@ -1,4 +1,6 @@
export function DirectoryIcon() {
import { cn } from "@/lib/utils"
export function DirectoryIcon({ className }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -6,7 +8,10 @@ export function DirectoryIcon() {
height="24"
viewBox="0 0 24 24"
fill="currentColor"
className="icon icon-tabler icons-tabler-filled icon-tabler-folder text-orange-300"
className={cn(
"icon icon-tabler icons-tabler-filled icon-tabler-folder text-orange-300",
className,
)}
aria-label="Directory"
>
<title>Directory</title>

View File

@@ -1,4 +1,6 @@
export function TextFileIcon() {
import { cn } from "@/lib/utils"
export function TextFileIcon({ className }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -6,7 +8,10 @@ export function TextFileIcon() {
height="24"
viewBox="0 0 24 24"
fill="currentColor"
className="icon icon-tabler icons-tabler-filled icon-tabler-file-text text-blue-300"
className={cn(
"icon icon-tabler icons-tabler-filled icon-tabler-file-text text-blue-300",
className,
)}
>
<title>Text File</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />

View File

@@ -72,6 +72,7 @@ const columns: ColumnDef<DirectoryItem>[] = [
),
enableSorting: false,
enableHiding: false,
size: 24,
},
{
header: "Name",
@@ -82,13 +83,14 @@ const columns: ColumnDef<DirectoryItem>[] = [
return <FileNameCell initialName={row.original.doc.name} />
case "directory":
return (
<div className="flex items-center gap-2">
<DirectoryIcon />
<div className="flex w-full items-center gap-2">
<DirectoryIcon className="size-4" />
{row.original.doc.name}
</div>
)
}
},
size: 1000,
},
{
header: "Size",
@@ -217,7 +219,10 @@ export function FileTableContent() {
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
<TableHead
key={header.id}
style={{ width: header.getSize() }}
>
{header.isPlaceholder
? null
: flexRender(
@@ -240,7 +245,10 @@ export function FileTableContent() {
}}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
<TableCell
key={cell.id}
style={{ width: cell.column.getSize() }}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
@@ -250,14 +258,7 @@ export function FileTableContent() {
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="text-center"
>
No results.
</TableCell>
</TableRow>
<NoResultsRow />
)}
<NewItemRow />
</TableBody>
@@ -266,6 +267,20 @@ export function FileTableContent() {
)
}
function NoResultsRow() {
const newItemKind = useAtomValue(newItemKindAtom)
if (newItemKind) {
return null
}
return (
<TableRow>
<TableCell colSpan={columns.length} className="text-center">
No results.
</TableCell>
</TableRow>
)
}
function NewItemRow() {
const inputRef = useRef<HTMLInputElement>(null)
const newItemFormId = useId()
@@ -363,8 +378,8 @@ function NewItemRow() {
function FileNameCell({ initialName }: { initialName: string }) {
return (
<div className="flex items-center gap-2">
<TextFileIcon />
<div className="flex w-full items-center gap-2">
<TextFileIcon className="size-4" />
{initialName}
</div>
)

View File

@@ -1,8 +1,6 @@
import { api } from "@convex/_generated/api"
import {
useMutation as useConvexMutation,
useQuery as useConvexQuery,
} from "convex/react"
import { useMutation } from "@tanstack/react-query"
import { useMutation as useConvexMutation } from "convex/react"
import { useSetAtom } from "jotai"
import {
ChevronDownIcon,
@@ -10,7 +8,7 @@ import {
PlusIcon,
UploadCloudIcon,
} from "lucide-react"
import { type ChangeEvent, useRef, useState } from "react"
import { type ChangeEvent, useRef } from "react"
import { toast } from "sonner"
import {
DropdownMenu,
@@ -57,10 +55,32 @@ export function FilesPage() {
// tags: upload, uploadfile, uploadfilebutton, fileupload, fileuploadbutton
function UploadFileButton() {
const [isUploading, setIsUploading] = useState(false)
const currentUser = useConvexQuery(api.users.getCurrentUser)
const generateUploadUrl = useConvexMutation(api.files.generateUploadUrl)
const saveFile = useConvexMutation(api.files.saveFile)
const { mutate: uploadFile, isPending: isUploading } = useMutation({
mutationFn: async (file: File) => {
const uploadUrl = await generateUploadUrl()
const uploadResult = await fetch(uploadUrl, {
method: "POST",
body: file,
headers: {
"Content-Type": file.type,
},
})
const { storageId } = await uploadResult.json()
await saveFile({
storageId,
name: file.name,
size: file.size,
mimeType: file.type,
})
},
onSuccess: () => {
toast.success("File uploaded successfully.")
},
})
const fileInputRef = useRef<HTMLInputElement>(null)
const handleClick = () => {
@@ -68,41 +88,9 @@ function UploadFileButton() {
}
const onFileUpload = async (e: ChangeEvent<HTMLInputElement>) => {
if (!currentUser?._id) {
return
}
const file = e.target.files?.[0]
if (file) {
try {
setIsUploading(true)
const uploadUrl = await generateUploadUrl()
const uploadResult = await fetch(uploadUrl, {
method: "POST",
body: file,
headers: {
"Content-Type": file.type,
},
})
const { storageId } = await uploadResult.json()
await saveFile({
storageId,
name: file.name,
size: file.size,
mimeType: file.type,
userId: currentUser._id,
})
toast.success("File uploaded successfully.", {
position: "top-center",
})
} catch (error) {
console.error(error)
} finally {
setIsUploading(false)
}
uploadFile(file)
}
}