Skip to content
Closed
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
84 changes: 78 additions & 6 deletions frontend/src/Performer/Details/PerformerDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import FieldSet from 'Components/FieldSet';
import Icon from 'Components/Icon';
import Label from 'Components/Label';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import MenuContent from 'Components/Menu/MenuContent';
import ViewMenu from 'Components/Menu/ViewMenu';
import ViewMenuItem from 'Components/Menu/ViewMenuItem';
import MonitorToggleButton from 'Components/MonitorToggleButton';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
Expand Down Expand Up @@ -38,6 +41,8 @@ import translate from 'Utilities/String/translate';
import EditPerformerModal from '../Edit/EditPerformerModal';
import PerformerDetailsLinks from './PerformerDetailsLinks';
import PerformerDetailsYear from './PerformerDetailsYear';
import PerformerPosterOptionsModal from './PerformerPosterOptionsModal';
import PerformerScenePosters from './PerformerScenePosters';
import PerformerTags from './PerformerTags';
import {
usePerformerDetails,
Expand Down Expand Up @@ -81,6 +86,12 @@ function PerformerDetails() {

const [isEditMovieModalOpen, setIsEditMovieModalOpen] = useState(false);
const [isDeleteMovieModalOpen, setIsDeleteMovieModalOpen] = useState(false);
const [isPosterOptionsModalOpen, setIsPosterOptionsModalOpen] =
useState(false);
const [view, setView] = useState<'table' | 'posters'>('table');
const [posterSize, setPosterSize] = useState<'small' | 'medium' | 'large'>(
'medium'
);
const [allExpandedYears, setAllExpandedYears] = useState<boolean>(false);

// Initialize expandedYears so current year is expanded by default
Expand Down Expand Up @@ -122,6 +133,12 @@ function PerformerDetails() {
setExpandedYears(newExpanded);
}

function handleViewChange(newView: string) {
if (newView === 'table' || newView === 'posters') {
setView(newView);
}
}

// Table columns for studios (from Redux, matches legacy connector)
const { columns } = usePerformerScenesColumns();
// Sorting state for all studios
Expand Down Expand Up @@ -230,6 +247,15 @@ function PerformerDetails() {
function handleDeleteMoviePress() {
setIsDeleteMovieModalOpen(true);
}
function handlePosterOptionsPress() {
setIsPosterOptionsModalOpen(true);
}
function handlePosterOptionsModalClose() {
setIsPosterOptionsModalOpen(false);
}
function handlePosterSizeChange(size: string) {
setPosterSize(size as 'small' | 'medium' | 'large');
}
function handleRefreshPress() {
onRefreshPress();
}
Expand Down Expand Up @@ -278,11 +304,41 @@ function PerformerDetails() {
onPress={handleDeleteMoviePress}
/>
<PageToolbarSection alignContent="right">
<PageToolbarButton
label="Expand All"
iconName={expandIcon}
onPress={handleExpandAllPress}
/>
{view === 'table' ? (
<PageToolbarButton
label="Expand All"
iconName={expandIcon}
onPress={handleExpandAllPress}
/>
) : null}

{view === 'posters' ? (
<PageToolbarButton
label={translate('Options')}
iconName={icons.POSTER}
onPress={handlePosterOptionsPress}
/>
) : null}

<ViewMenu alignMenu="right">
<MenuContent>
<ViewMenuItem
name="table"
selectedView={view}
onPress={handleViewChange}
>
{translate('Table')}
</ViewMenuItem>

<ViewMenuItem
name="posters"
selectedView={view}
onPress={handleViewChange}
>
{translate('Posters')}
</ViewMenuItem>
</MenuContent>
</ViewMenu>
</PageToolbarSection>
</PageToolbar>
<PageContentBody innerClassName={styles.innerContentBody}>
Expand Down Expand Up @@ -527,7 +583,7 @@ function PerformerDetails() {
</Alert>
) : null}
{/* Studios section (delayed render for each studio) */}
{movies.length > 0 && (
{movies.length > 0 && view === 'table' && (
<FieldSet legend={translate('Works')}>
{moviesByYear.map(({ year, movies: yearMovies }) => (
<PerformerDetailsYear
Expand All @@ -548,6 +604,16 @@ function PerformerDetails() {
))}
</FieldSet>
)}
{movies.length > 0 && view === 'posters' && (
<FieldSet legend={translate('Works')}>
<PerformerScenePosters
movies={movies}
sortKey={sortKey}
sortDirection={sortDirection}
posterSize={posterSize}
/>
</FieldSet>
)}
</div>
<DeletePerformerModal
isOpen={isDeleteMovieModalOpen}
Expand All @@ -560,6 +626,12 @@ function PerformerDetails() {
showMovieMonitor={showMovieMonitorToggle}
onModalClose={handleEditMovieModalClose}
/>
<PerformerPosterOptionsModal
isOpen={isPosterOptionsModalOpen}
size={posterSize}
onSizeChange={handlePosterSizeChange}
onModalClose={handlePosterOptionsModalClose}
/>
</PageContentBody>
</PageContent>
);
Expand Down
29 changes: 29 additions & 0 deletions frontend/src/Performer/Details/PerformerPosterOptionsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import Modal from 'Components/Modal/Modal';
import PerformerPosterOptionsModalContent from './PerformerPosterOptionsModalContent';

interface PerformerPosterOptionsModalProps {
isOpen: boolean;
size: string;
onSizeChange(size: string): void;
onModalClose(...args: unknown[]): unknown;
}

function PerformerPosterOptionsModal({
isOpen,
size,
onSizeChange,
onModalClose,
}: PerformerPosterOptionsModalProps) {
return (
<Modal isOpen={isOpen} onModalClose={onModalClose}>
<PerformerPosterOptionsModalContent
size={size}
onSizeChange={onSizeChange}
onModalClose={onModalClose}
/>
</Modal>
);
}

export default PerformerPosterOptionsModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, { useCallback } from 'react';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import Button from 'Components/Link/Button';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';

const posterSizeOptions = [
{
key: 'small',
get value() {
return translate('Small');
},
},
{
key: 'medium',
get value() {
return translate('Medium');
},
},
{
key: 'large',
get value() {
return translate('Large');
},
},
];

interface PerformerPosterOptionsModalContentProps {
size: string;
onSizeChange(size: string): void;
onModalClose(...args: unknown[]): unknown;
}

function PerformerPosterOptionsModalContent(
props: PerformerPosterOptionsModalContentProps
) {
const { size, onSizeChange, onModalClose } = props;

const onPosterOptionChange = useCallback(
({ value }: { name: string; value: string }) => {
onSizeChange(value);
},
[onSizeChange]
);

return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>{translate('PosterOptions')}</ModalHeader>

<ModalBody>
<Form>
<FormGroup>
<FormLabel>{translate('PosterSize')}</FormLabel>

<FormInputGroup
type={inputTypes.SELECT}
name="size"
value={size}
values={posterSizeOptions}
onChange={onPosterOptionChange}
/>
</FormGroup>
</Form>
</ModalBody>

<ModalFooter>
<Button onPress={onModalClose}>{translate('Close')}</Button>
</ModalFooter>
</ModalContent>
);
}

export default PerformerPosterOptionsModalContent;
57 changes: 57 additions & 0 deletions frontend/src/Performer/Details/PerformerScenePoster.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.content {
transition: all 200ms ease-in;

&:hover {
z-index: 2;
box-shadow: 0 0 12px var(--black);
transition: all 200ms ease-in;
}
}

.posterContainer {
position: relative;
}

.link {
composes: link from '~Components/Link/Link.css';

position: relative;
display: block;
background-color: var(--defaultColor);
}

.poster {
object-fit: cover;
}

.overlayTitle {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 5px;
width: 100%;
height: 100%;
color: var(--offWhite);
text-align: center;
font-size: 20px;
}

.title {
@add-mixin truncate;

background-color: var(--sceneBackgroundColor);
text-align: center;
font-size: $smallFontSize;
}

.info {
@add-mixin truncate;

background-color: var(--sceneBackgroundColor);
color: var(--dimColor);
text-align: center;
font-size: $extraSmallFontSize;
}
13 changes: 13 additions & 0 deletions frontend/src/Performer/Details/PerformerScenePoster.css.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'content': string;
'info': string;
'link': string;
'overlayTitle': string;
'poster': string;
'posterContainer': string;
'title': string;
}
export const cssExports: CssExports;
export default cssExports;
Loading
Loading