Added ability to rename location

This commit is contained in:
Donkie
2024-11-20 21:00:39 +01:00
parent 5861190821
commit 37b6d1fcee
5 changed files with 103 additions and 5 deletions

View File

@@ -33,3 +33,19 @@ export function useLocations(): string[] {
} }
}, [query.data]); }, [query.data]);
} }
export async function renameSpoolLocation(location: string, newName: string): Promise<string> {
const response = await fetch(getAPIURL() + "/location/" + location, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: newName,
}),
});
if (!response.ok) {
throw new Error("Network response was not ok");
}
return await response.text();
}

View File

@@ -1,9 +1,9 @@
import { IResourceComponentsProps, useInvalidate, useList, useNavigation, useTranslate } from "@refinedev/core"; import { IResourceComponentsProps, useInvalidate, useList, useNavigation, useTranslate } from "@refinedev/core";
import { Button, theme } from "antd"; import { Button, Input, theme } from "antd";
import dayjs from "dayjs"; import dayjs from "dayjs";
import utc from "dayjs/plugin/utc"; import utc from "dayjs/plugin/utc";
import type { Identifier, XYCoord } from "dnd-core"; import type { Identifier, XYCoord } from "dnd-core";
import React, { useEffect, useMemo, useRef } from "react"; import React, { useEffect, useMemo, useRef, useState } from "react";
import { DndProvider, useDrag, useDrop } from "react-dnd"; import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend"; import { HTML5Backend } from "react-dnd-html5-backend";
@@ -12,7 +12,7 @@ import { Link } from "react-router-dom";
import SpoolIcon from "../../components/spoolIcon"; import SpoolIcon from "../../components/spoolIcon";
import { useSetSetting } from "../../utils/querySettings"; import { useSetSetting } from "../../utils/querySettings";
import { ISpool } from "../spools/model"; import { ISpool } from "../spools/model";
import { setSpoolLocation, useLocations } from "./functions"; import { renameSpoolLocation, setSpoolLocation, useLocations } from "./functions";
import "./locations.css"; import "./locations.css";
dayjs.extend(utc); dayjs.extend(utc);
@@ -130,6 +130,7 @@ function LocationContainer({
showDelete, showDelete,
onDelete, onDelete,
moveLocation, moveLocation,
onEditTitle,
}: { }: {
index: number; index: number;
title: string; title: string;
@@ -137,7 +138,11 @@ function LocationContainer({
showDelete?: boolean; showDelete?: boolean;
onDelete?: () => void; onDelete?: () => void;
moveLocation: (dragIndex: number, hoverIndex: number) => void; moveLocation: (dragIndex: number, hoverIndex: number) => void;
onEditTitle: (newTitle: string) => void;
}) { }) {
const [editTitle, setEditTitle] = useState(false);
const [newTitle, setNewTitle] = useState(title);
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const [{ handlerId }, drop] = useDrop<DragItem, void, { handlerId: Identifier | null }>({ const [{ handlerId }, drop] = useDrop<DragItem, void, { handlerId: Identifier | null }>({
accept: ItemTypes.CONTAINER, accept: ItemTypes.CONTAINER,
@@ -189,6 +194,7 @@ function LocationContainer({
const [{ isDragging }, drag] = useDrag({ const [{ isDragging }, drag] = useDrag({
type: ItemTypes.CONTAINER, type: ItemTypes.CONTAINER,
canDrag: !editTitle,
item: () => { item: () => {
return { title, index }; return { title, index };
}, },
@@ -203,7 +209,28 @@ function LocationContainer({
return ( return (
<div className="loc-container" ref={ref} style={{ opacity }} data-handler-id={handlerId}> <div className="loc-container" ref={ref} style={{ opacity }} data-handler-id={handlerId}>
<h3> <h3>
<span>{title}</span> {editTitle ? (
<Input
autoFocus
variant="borderless"
value={newTitle}
onBlur={() => setEditTitle(false)}
onChange={(e) => setNewTitle(e.target.value)}
onPressEnter={() => {
setEditTitle(false);
return onEditTitle(newTitle);
}}
/>
) : (
<span
onClick={() => {
setNewTitle(title);
setEditTitle(true);
}}
>
{title}
</span>
)}
{showDelete && <Button icon={<DeleteOutlined />} size="small" type="text" onClick={onDelete} />} {showDelete && <Button icon={<DeleteOutlined />} size="small" type="text" onClick={onDelete} />}
</h3> </h3>
<SpoolList location={title} spools={spools} /> <SpoolList location={title} spools={spools} />
@@ -213,6 +240,7 @@ function LocationContainer({
function LocationMetaContainer() { function LocationMetaContainer() {
const t = useTranslate(); const t = useTranslate();
const invalidate = useInvalidate();
const settingsLocations = useLocations(); const settingsLocations = useLocations();
const setLocationsSetting = useSetSetting<string[]>("locations"); const setLocationsSetting = useSetSetting<string[]>("locations");
@@ -266,7 +294,23 @@ function LocationMetaContainer() {
const newLocs = [...locationsList]; const newLocs = [...locationsList];
newLocs.splice(dragIndex, 1); newLocs.splice(dragIndex, 1);
newLocs.splice(hoverIndex, 0, locationsList[dragIndex]); newLocs.splice(hoverIndex, 0, locationsList[dragIndex]);
console.log("newLocs", newLocs); setLocationsSetting.mutate(newLocs);
};
const onEditTitle = async (location: string, newTitle: string) => {
if (newTitle == location) return;
if (newTitle == "") return;
if (locationsList.includes(newTitle)) return;
// Update all spool locations in the database
if (spoolLocations[location] && spoolLocations[location].length > 0) {
await renameSpoolLocation(location, newTitle);
await invalidate({ resource: "spool", invalidates: ["list", "detail"] });
}
// Update the value in the settings
const newLocs = [...locationsList];
newLocs[locationsList.indexOf(location)] = newTitle;
setLocationsSetting.mutate(newLocs); setLocationsSetting.mutate(newLocs);
}; };
@@ -284,6 +328,7 @@ function LocationMetaContainer() {
setLocationsSetting.mutate(locationsList.filter((l) => l !== loc)); setLocationsSetting.mutate(locationsList.filter((l) => l !== loc));
}} }}
moveLocation={moveLocation} moveLocation={moveLocation}
onEditTitle={(newTitle: string) => onEditTitle(loc, newTitle)}
/> />
); );
}); });

View File

@@ -6,6 +6,7 @@
.loc-container { .loc-container {
padding: 1em; padding: 1em;
width: 24em; width: 24em;
cursor: pointer;
} }
.loc-container h3 { .loc-container h3 {
@@ -13,6 +14,7 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
cursor: text;
} }
.loc-container .loc-spools { .loc-container .loc-spools {

View File

@@ -4,6 +4,7 @@ import logging
from typing import Annotated from typing import Annotated
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from pydantic import BaseModel, Field, RootModel
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from spoolman.database import filament, spool from spoolman.database import filament, spool
@@ -123,3 +124,25 @@ async def find_locations(
db: Annotated[AsyncSession, Depends(get_db_session)], db: Annotated[AsyncSession, Depends(get_db_session)],
) -> list[str]: ) -> list[str]:
return await spool.find_locations(db=db) return await spool.find_locations(db=db)
class RenameLocationBody(BaseModel):
name: str = Field(description="The new name of the location.", min_length=1)
@router.patch(
"/location/{location}",
name="Rename location",
description="Rename a spool location. All spools in this location will be moved to the new location.",
response_model_exclude_none=True,
response_model=RootModel[str],
)
async def rename_location(
location: str,
*,
db: Annotated[AsyncSession, Depends(get_db_session)],
body: RenameLocationBody,
) -> str:
logger.info("Renaming location %s to %s", location, body.name)
await spool.rename_location(db=db, current_name=location, new_name=body.name)
return body.name

View File

@@ -462,3 +462,15 @@ async def reset_initial_weight(db: AsyncSession, spool_id: int, weight: float) -
await db.commit() await db.commit()
await spool_changed(spool, EventType.UPDATED) await spool_changed(spool, EventType.UPDATED)
return spool return spool
async def rename_location(
*,
db: AsyncSession,
current_name: str,
new_name: str,
) -> None:
"""Rename all spools with the current location name to the new name."""
await db.execute(
sqlalchemy.update(models.Spool).where(models.Spool.location == current_name).values(location=new_name),
)