This commit is contained in:
Spokeek 2026-06-18 14:00:21 +00:00 committed by GitHub
commit fea57e8ee6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 215 additions and 6 deletions

View file

@ -37,6 +37,7 @@ import {
import { openSingleDialog } from "@/lib/dialog";
import { tc } from "@/lib/i18n";
import { toastThrownError } from "@/lib/toast";
import { openProjectDetails } from "@/app/_main/projects/manage/-edit-project-details";
export function ProjectGridItem({
project,
@ -88,6 +89,21 @@ export function ProjectGridItem({
>
{tc("projects:menuitem:open directory")}
</DropdownMenuItem>
<DropdownMenuItem
onClick={async () => {
try {
await openProjectDetails({
existingMemo: project.memo,
existingTags: project.tags
})
} catch (e) {
console.error(e);
toastThrownError(e);
}
}}
>
{tc("projects:menuitem:project details")}
</DropdownMenuItem>
<DropdownMenuItem
onClick={async () => {
try {
@ -123,12 +139,24 @@ export function ProjectGridItem({
<TooltipTriggerIfValid
className={"text-left select-text cursor-auto w-full"}
>
<p className="font-normal whitespace-pre overflow-ellipsis overflow-hidden">
<p className="font-normal whitespace-pre overflow-ellipsis overflow-hidden w-100">
{project.name}
</p>
<p className="font-normal opacity-50 text-sm whitespace-pre overflow-ellipsis overflow-hidden compact:hidden">
{project.path}
Path: {project.path}
</p>
{!project.memo ? undefined : (
<p className="font-normal opacity-50 text-sm whitespace-pre overflow-ellipsis overflow-hidden compact:hidden">
Memo: {project.memo}
</p>
)}
{Array.isArray(project.tags) && project.tags.length != 0 && (
<div className="flex overflow-x-auto gap-2 mt-1">
{project.tags.map(tag => (
<Button variant={"info"} size={"sm"}>{tag}</Button>
))}
</div>
)}
</TooltipTriggerIfValid>
<TooltipContent>{project.path}</TooltipContent>
</Tooltip>

View file

@ -40,6 +40,7 @@ import { router } from "@/lib/main";
import { queryClient } from "@/lib/query-client";
import { toastError, toastSuccess, toastThrownError } from "@/lib/toast";
import { compareUnityVersionString } from "@/lib/version";
import { openProjectDetails } from "./manage/-edit-project-details";
export const ProjectDisplayType: Record<
TauriProjectType,
@ -135,8 +136,20 @@ export function ProjectRow({
>
<p className="font-normal whitespace-pre">{project.name}</p>
<p className="font-normal opacity-50 text-sm whitespace-pre compact:hidden">
{project.path}
Path: {project.path}
</p>
{!project.memo ? undefined : (
<p className="font-normal opacity-50 text-sm whitespace-pre overflow-ellipsis overflow-hidden compact:hidden">
Memo: {project.memo}
</p>
)}
{Array.isArray(project.tags) && project.tags.length != 0 && (
<div className="flex overflow-x-auto gap-2 mt-1">
{project.tags.map(tag => (
<Button variant={"info"} size={"sm"}>{tag}</Button>
))}
</div>
)}
</TooltipTriggerIfValid>
<TooltipContent>{project.path}</TooltipContent>
</Tooltip>
@ -251,6 +264,21 @@ export function ProjectRow({
>
{tc("projects:menuitem:open directory")}
</DropdownMenuItem>
<DropdownMenuItem
onClick={async () => {
try {
await openProjectDetails({
existingMemo: project.memo,
existingTags: project.tags
})
} catch (e) {
console.error(e);
toastThrownError(e);
}
}}
>
{tc("projects:menuitem:project details")}
</DropdownMenuItem>
<DropdownMenuItem
onClick={onCopyProject}
disabled={removed || !(is_valid ?? true)}

View file

@ -205,9 +205,15 @@ export function sortSearchProjects(
search: string,
sorting: Sorting,
): TauriProject[] {
const searched = projects.filter((project) =>
project.name.toLowerCase().includes(search?.toLowerCase() ?? ""),
);
const searched = projects.filter((project) => {
const searchString = search?.toLowerCase() ?? ""
return (
project.name.toLowerCase().includes(searchString) ||
project.memo.toLowerCase().includes(searchString) ||
project.tags?.map(tag => tag.toLowerCase()).find(tag => tag.includes(searchString))
)
});
searched.sort((a, b) => b.last_modified - a.last_modified);

View file

@ -46,6 +46,38 @@ const environmentProjects = queryOptions({
function Page() {
const result = useQuery(environmentProjects);
//TODO remove temporary Mock
if(!result.error){
result.data = result.data?.map(project => {
const id = crypto.randomUUID()
project.name = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx "+id
project.path = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy "+id
if (Math.random() > 0.5) {
project.memo = ""
project.tags = []
return project
}
project.memo = "Super memo with much details n°"+id
if(project.tags == null){
project.tags = []
project.tags.push("Spokeek")
if (Math.random() > 0.3) project.tags.push("Number "+Math.ceil(Math.random()*1000))
if (Math.random() > 0.3) project.tags.push("Emoji 🔥")
if (Math.random() > 0.3) project.tags.push("Chinese 本前开但因只从想")
if (Math.random() > 0.3) project.tags.push("Cursed ḑ̸̨͈̬̲̜̟̤͉̪̪̞̖̏̄̀̓̈́́͐̈́̓̆̎͜z̷͙̣̘̰̻̲͔̻͕͕͖̙͎̿̾͒͒͌̀̍͒̔̏̿̏̚͘a̸̡̡̧̨͓͈͖͎̹̰͕̲͍̾̀͂͆̽̅͗̒̑̍͘̚̚͜d̵̨̧̡̰̻͖̯̱͖͖̰̘́̽͂͆̀̍͗̾̂̐͐̑̏â̷̲̈́͊̌͛̉̇̇ͅd̵̜̠̳͎̙̝͚̓̅̋̆͐͘͜͠͝͝")
if (Math.random() > 0.5)
project.tags.push("Random 0.5")
if (Math.random() > 0.7)
project.tags.push("Super Random 0.7")
}
return project
})
}
const [search, setSearch] = useState("");
const viewModeQuery = useQuery({

View file

@ -0,0 +1,111 @@
import { type DialogContext, showDialog } from "@/lib/dialog";
import { DialogFooter, DialogTitle } from "@/components/ui/dialog";
import { tc } from "@/lib/i18n";
import { NavigateFn } from "@tanstack/react-router";
import { Button } from "@/components/ui/button";
import { useState } from "react";
import { Input } from "@/components/ui/input";
import { toastError } from "@/lib/toast";
export async function openProjectDetails({
existingTags,
existingMemo
}: {
existingTags: string[],
existingMemo: string
}, navigate?: NavigateFn) {
using dialog = showDialog();
await dialog.ask(openProjectDetailsDialog, { existingTags, existingMemo });
}
function saveProjectDetails({
newMemo,
newTags
}:
{
newMemo: string,
newTags: string[]
}) {
console.log({
newMemo,
newTags
})
toastError("Feature is still being developped")
}
function openProjectDetailsDialog({
dialog,
existingTags,
existingMemo
}: {
dialog: DialogContext<string | null>,
existingTags: string[],
existingMemo: string
}) {
const [modified, setModified] = useState(false)
const [newMemo, setNewMemo] = useState(existingMemo)
const [newTags, setNewTags] = useState(existingTags.map(((tag, index) => ({ index, value: tag }))))
function resetFields() {
setNewMemo(existingMemo)
setNewTags(existingTags.map(((tag, index) => ({ index, value: tag }))))
setModified(false)
}
return (<>
<DialogTitle>{tc("projects:menuitem:project details")}</DialogTitle>
<section>
<h3 className="mb-1">
Memo
</h3>
<Input className="w-full min-h-5" value={newMemo} placeholder={newMemo} onChange={(changeEvent => {
setModified(true)
setNewMemo(changeEvent.target.value)
})} />
</section>
<section className="mb-2">
<h3 className="mb-1">
Tags
</h3>
<ul>
{newTags.map((newTag) => (<li>
<Input
key={newTag.index}
className="w-full mb-1"
value={newTag.value}
placeholder={newTag.value}
onChange={(updateEvent) => {
setModified(true)
setNewTags(newTags.map(tag => {
if (tag.index === newTag.index)
tag.value = updateEvent.target.value
return tag
}))
}}
/>
</li>))}
</ul>
</section>
<DialogFooter className={"gap-2"}>
<Button disabled={!modified} onClick={resetFields}>
{tc("general:button:reset")}
</Button>
<Button onClick={() => dialog.close(null)}>
{tc("general:button:cancel")}
</Button>
{/* button save not working */}
<Button
disabled={!modified}
onClick={() => {
saveProjectDetails({
newMemo,
newTags: newTags.map(tag => tag.value)
})
}}
>
{tc("project details:buttons:confirm")}
</Button>
</DialogFooter>
</>)
}

View file

@ -565,5 +565,9 @@
"logs:message": "Message",
"logs:manage:select logs level": "Logs Level Filter",
"logs:manage:auto scroll": "Toggle Auto Scroll",
// Project details
"projects:menuitem:project details": "Edit project details",
"project details:buttons:confirm": "Update details"
},
}