diff --git a/ui/v2.5/src/components/Shared/StashBoxIDSearchModal.tsx b/ui/v2.5/src/components/Shared/StashBoxIDSearchModal.tsx index 2e98907d2..88544d685 100644 --- a/ui/v2.5/src/components/Shared/StashBoxIDSearchModal.tsx +++ b/ui/v2.5/src/components/Shared/StashBoxIDSearchModal.tsx @@ -14,15 +14,17 @@ import { Icon } from "src/components/Shared/Icon"; import { stashBoxPerformerQuery, stashBoxSceneQuery, + stashBoxStudioQuery, } from "src/core/StashService"; import { useToast } from "src/hooks/Toast"; import { stringToGender } from "src/utils/gender"; type SearchResultItem = | GQL.ScrapedPerformerDataFragment - | GQL.ScrapedSceneDataFragment; + | GQL.ScrapedSceneDataFragment + | GQL.ScrapedStudioDataFragment; -export type StashBoxEntityType = "performer" | "scene"; +export type StashBoxEntityType = "performer" | "scene" | "studio"; interface IProps { entityType: StashBoxEntityType; @@ -190,6 +192,46 @@ export const SceneSearchResult: React.FC = ({ scene }) => { ); }; +// Studio Result Component +interface IStudioResultProps { + studio: GQL.ScrapedStudioDataFragment; +} + +const StudioSearchResultDetails: React.FC = ({ + studio, +}) => { + return ( +
+ + +
+

+ {studio.name} +

+ {studio.parent?.name && ( +
+ {studio.parent.name} +
+ )} + {studio.urls && studio.urls.length > 0 && ( +
{studio.urls[0]}
+ )} +
+
+
+ ); +}; + +export const StudioSearchResult: React.FC = ({ + studio, +}) => { + return ( +
+ +
+ ); +}; + // Helper to get entity type display name for i18n function getEntityTypeDisplayName(entityType: StashBoxEntityType): string { switch (entityType) { @@ -197,6 +239,8 @@ function getEntityTypeDisplayName(entityType: StashBoxEntityType): string { return "Performer"; case "scene": return "Scene"; + case "studio": + return "Studio"; } } @@ -207,6 +251,8 @@ function getFoundMessageId(entityType: StashBoxEntityType): string { return "dialogs.performers_found"; case "scene": return "dialogs.scenes_found"; + case "studio": + return "dialogs.studios_found"; } } @@ -264,6 +310,14 @@ export const StashBoxIDSearchModal: React.FC = ({ setResults(queryData.data?.scrapeSingleScene ?? []); break; } + case "studio": { + const queryData = await stashBoxStudioQuery( + query, + selectedStashBox.endpoint + ); + setResults(queryData.data?.scrapeSingleStudio ?? []); + break; + } } } catch (error) { Toast.error(error); @@ -299,6 +353,10 @@ export const StashBoxIDSearchModal: React.FC = ({ return ( ); + case "studio": + return ( + + ); } } diff --git a/ui/v2.5/src/components/Studios/StudioDetails/StudioEditPanel.tsx b/ui/v2.5/src/components/Studios/StudioDetails/StudioEditPanel.tsx index 264afdc7c..a4b9a62aa 100644 --- a/ui/v2.5/src/components/Studios/StudioDetails/StudioEditPanel.tsx +++ b/ui/v2.5/src/components/Studios/StudioDetails/StudioEditPanel.tsx @@ -5,18 +5,22 @@ import * as yup from "yup"; import Mousetrap from "mousetrap"; import { LoadingIndicator } from "src/components/Shared/LoadingIndicator"; import { DetailsEditNavbar } from "src/components/Shared/DetailsEditNavbar"; -import { Form } from "react-bootstrap"; +import { Button, Form } from "react-bootstrap"; +import { faPlus } from "@fortawesome/free-solid-svg-icons"; import ImageUtils from "src/utils/image"; import { getStashIDs } from "src/utils/stashIds"; import { useFormik } from "formik"; import { Prompt } from "react-router-dom"; import isEqual from "lodash-es/isEqual"; import { useToast } from "src/hooks/Toast"; +import { useConfigurationContext } from "src/hooks/Config"; import { handleUnsavedChanges } from "src/utils/navigation"; import { formikUtils } from "src/utils/form"; import { yupFormikValidate, yupUniqueAliases } from "src/utils/yup"; import { Studio, StudioSelect } from "../StudioSelect"; import { useTagsEdit } from "src/hooks/tagsEdit"; +import { Icon } from "src/components/Shared/Icon"; +import StashBoxIDSearchModal from "src/components/Shared/StashBoxIDSearchModal"; interface IStudioEditPanel { studio: Partial; @@ -37,9 +41,13 @@ export const StudioEditPanel: React.FC = ({ }) => { const intl = useIntl(); const Toast = useToast(); + const { configuration: stashConfig } = useConfigurationContext(); const isNew = studio.id === undefined; + // Editing state + const [isStashIDSearchOpen, setIsStashIDSearchOpen] = useState(false); + // Network state const [isLoading, setIsLoading] = useState(false); @@ -143,6 +151,24 @@ export const StudioEditPanel: React.FC = ({ ImageUtils.onImageChange(event, onImageLoad); } + function onStashIDSelected(item?: GQL.StashIdInput) { + if (!item) return; + + const existingIndex = formik.values.stash_ids.findIndex( + (s) => s.endpoint === item.endpoint + ); + + let newStashIDs; + if (existingIndex >= 0) { + newStashIDs = [...formik.values.stash_ids]; + newStashIDs[existingIndex] = item; + } else { + newStashIDs = [...formik.values.stash_ids, item]; + } + + formik.setFieldValue("stash_ids", newStashIDs); + } + const { renderField, renderInputField, @@ -173,6 +199,20 @@ export const StudioEditPanel: React.FC = ({ return ( <> + {isStashIDSearchOpen && ( + s.endpoint + )} + onSelectItem={(item) => { + onStashIDSelected(item); + setIsStashIDSearchOpen(false); + }} + /> + )} + { @@ -191,7 +231,21 @@ export const StudioEditPanel: React.FC = ({ {renderInputField("details", "textarea")} {renderParentStudioField()} {renderTagsField()} - {renderStashIDsField("stash_ids", "studios")} + {renderStashIDsField( + "stash_ids", + "studios", + "stash_ids", + undefined, + + )}
{renderInputField("ignore_auto_tag", "checkbox")} diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index 37ef0d12b..c6dbddd26 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -1014,6 +1014,7 @@ "video_previews_tooltip": "Video previews which play when hovering over a scene" }, "scenes_found": "{count} scenes found", + "studios_found": "{count} studios found", "scrape_entity_query": "{entity_type} Scrape Query", "scrape_entity_title": "{entity_type} Scrape Results", "scrape_results_existing": "Existing",