Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
4rthem committed Jun 12, 2024
1 parent 646de1b commit b7e1e25
Show file tree
Hide file tree
Showing 19 changed files with 513 additions and 29 deletions.
8 changes: 7 additions & 1 deletion databox/api/src/Elasticsearch/AssetSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public function search(
array $groupIds,
array $options = []
): array {
$maxLimit = 50;

$filterQueries = [];

$aclBoolQuery = $this->createACLBoolQuery($userId, $groupIds);
Expand All @@ -50,6 +52,11 @@ public function search(
$filterQueries[] = new Query\Terms('collectionPaths', $paths);
}

if (isset($options['ids'])) {
$filterQueries[] = new Query\Terms('_id', $options['ids']);
$maxLimit = 500;
}

if (isset($options['workspaces'])) {
$filterQueries[] = new Query\Terms('workspaceId', $options['workspaces']);
}
Expand Down Expand Up @@ -82,7 +89,6 @@ public function search(
}
}

$maxLimit = 50;
$limit = $options['limit'] ?? $maxLimit;
if ($limit > $maxLimit) {
$limit = $maxLimit;
Expand Down
1 change: 1 addition & 0 deletions databox/client/src/api/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface GetAssetOptions {
url?: string;
query?: string;
workspaces?: string[];
ids?: string[];
parents?: string[];
filters?: any;
order?: Record<string, 'asc' | 'desc'>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,11 @@ export default function SelectionActions<Item extends AssetOrAssetContainer>({
id: selectedAssets[0].id,
});
} else {
alert('Multi edit is coming soon...');
navigateToModal(modalRoutes.attributesBatchEdit, {}, {
state: {
selection: selectedAssets.map(a => a.id),
}
});
}
};

Expand All @@ -185,7 +189,11 @@ export default function SelectionActions<Item extends AssetOrAssetContainer>({
id: selectedAssets[0].id,
});
} else {
alert('Multi edit attributes is coming soon...');
navigateToModal(modalRoutes.attributesBatchEdit, {}, {
state: {
selection: selectedAssets.map(a => a.id),
}
});
}
};

Expand Down
2 changes: 1 addition & 1 deletion databox/client/src/components/AssetList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export type AssetActions<Item extends AssetOrAssetContainer> = {
};

export type AssetItemProps<Item extends AssetOrAssetContainer> = {
itemComponent: AssetItemComponent<Item> | undefined;
itemComponent?: AssetItemComponent<Item> | undefined;
item: Item;
asset: Asset;
selected: boolean;
Expand Down
68 changes: 68 additions & 0 deletions databox/client/src/components/AttributeEditor/AssetItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {Asset} from "../../types.ts";
import AssetItemWrapper from "../AssetList/Layouts/AssetItemWrapper.tsx";
import assetClasses from "../AssetList/classes.ts";
import {Checkbox} from "@mui/material";
import {stopPropagation} from "../../lib/stdFuncs.ts";
import React from "react";
import AssetThumb from "../Media/Asset/AssetThumb.tsx";
import {OnPreviewToggle, OnToggle} from "../AssetList/types.ts";

type Props = {
asset: Asset;
selected: boolean;
onToggle: OnToggle<Asset>;
onPreviewToggle: OnPreviewToggle;
};

export default function AssetItem({
asset,
selected,
onToggle,
onPreviewToggle,
}: Props) {
return (
<AssetItemWrapper<Asset>
item={asset}
onToggle={onToggle}
selected={selected}
>
<div className={assetClasses.controls}>
<Checkbox
className={assetClasses.checkBtb}
checked={selected}
color={'primary'}
onMouseDown={stopPropagation}
onChange={() =>
onToggle(asset, {
ctrlKey: true,
preventDefault() {},
} as React.MouseEvent)
}
/>
</div>
<AssetThumb
asset={asset}
onMouseOver={
onPreviewToggle
? e =>
onPreviewToggle(
asset,
true,
e.currentTarget as HTMLElement
)
: undefined
}
onMouseLeave={
onPreviewToggle
? e =>
onPreviewToggle(
asset,
false,
e.currentTarget as HTMLElement
)
: undefined
}
/>
</AssetItemWrapper>
);
}
88 changes: 88 additions & 0 deletions databox/client/src/components/AttributeEditor/AttributeEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import {Asset, AttributeDefinition} from "../../types.ts";
import {useTranslation} from "react-i18next";
import {getAssets} from "../../api/asset.ts";
import {Box, Typography} from "@mui/material";
import {FullPageLoader} from "@alchemy/phrasea-ui";
import React from "react";
import ThumbList from "./ThumbList.tsx";
import {AssetSelectionContext} from "../../context/AssetSelectionContext.tsx";
import DisplayProvider from "../Media/DisplayProvider.tsx";
import {OnToggle} from "../AssetList/types.ts";
import {getItemListFromEvent} from "../AssetList/selection.ts";
import Attributes from "./Attributes.tsx";
import {getWorkspaceAttributeDefinitions} from "../../api/attributes.ts";

type Props = {
ids: string[];
};

export default function AttributeEditor({
ids,
}: Props) {
const {t} = useTranslation();
const [assets, setAssets] = React.useState<Asset[]>();
const [subSelection, setSubSelection] = React.useState<Asset[]>([]);
const [attributeDefinitions, setAttributeDefinitions] = React.useState<AttributeDefinition[]>();

const onToggleAsset = React.useCallback<OnToggle<Asset>>(
(asset, e): void => {
e?.preventDefault();
setSubSelection(prev => {
return getItemListFromEvent(prev, asset, [assets!], e);
});
},
[assets]
);

React.useEffect(() => {
getAssets({
ids,
}).then(r => {
setAssets(r.result);
setSubSelection(r.result);
});
}, [ids]);

React.useEffect(() => {
if (assets) {
getWorkspaceAttributeDefinitions(assets[0].workspace.id).then(r => {
setAttributeDefinitions(r);
});
}
}, [assets]);

if (!assets) {
return <FullPageLoader/>
}

return <Box
sx={{
height: '100vh',
overflow: 'hidden',
}}
>
<Typography variant={'h1'}>
{t('attribute.editor.title', 'Attribute Editor')}
</Typography>
<DisplayProvider>
<AssetSelectionContext.Provider
value={{
selection: subSelection!,
setSelection: setSubSelection,
}}
>
<ThumbList
assets={assets}
onToggle={onToggleAsset}
subSelection={subSelection}
/>

{attributeDefinitions ? <Attributes
attributeDefinitions={attributeDefinitions}
assets={assets}
subSelection={subSelection}
/> : <>Loading attributes...</>}
</AssetSelectionContext.Provider>
</DisplayProvider>
</Box>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {useLocation} from '@alchemy/navigation';
import {AppDialog} from "@alchemy/phrasea-ui";
import RouteDialog from "../Dialog/RouteDialog.tsx";
import AttributeEditor from "./AttributeEditor.tsx";
import {useCloseModal} from "../Routing/ModalLink.tsx";
import React from "react";

type Props = {};

export default function AttributeEditorView({}: Props) {
const {state} = useLocation();
const closeDrawer = useCloseModal();

React.useEffect(() => {
if (!state?.selection) {
closeDrawer({
replace: true,
});
}
}, [state]);

if (!state?.selection) {
return <></>;
}

return (
<RouteDialog>
{({open, onClose}) => (
<AppDialog
open={open}
disablePadding={true}
fullScreen={true}
onClose={onClose}
>
<AttributeEditor
ids={state.selection}
/>
</AppDialog>
)}
</RouteDialog>
);
}
76 changes: 76 additions & 0 deletions databox/client/src/components/AttributeEditor/Attributes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {Asset, AttributeDefinition} from "../../types.ts";
import {useAttributeValues} from "./attributeGroup.ts";
import {Box, ListItem, ListItemButton, TextField} from "@mui/material";
import React from "react";

type Props = {
assets: Asset[];
subSelection: Asset[];
attributeDefinitions: AttributeDefinition[];
};

export default function Attributes({
assets,
attributeDefinitions,
subSelection,
}: Props) {
const {values, setValue} = useAttributeValues(attributeDefinitions, assets, subSelection);
const inputRef = React.useRef<HTMLInputElement | null>(null);
const [definition, setDefinition] = React.useState<AttributeDefinition | undefined>(attributeDefinitions[0]);

const value = definition ? values[definition.id] : undefined;

React.useEffect(() => {
inputRef.current?.focus();
}, [definition]);

return <div
style={{
display: 'flex',
flexGrow: 1,
}}
>
<Box sx={{
width: 300,
'strong': {
mr: 1,
verticalAlign: 'top',
alignSelf: 'start',
}
}}>
{attributeDefinitions.map((def) => {
return <ListItem
disablePadding
key={def.id}
>
<ListItemButton
selected={definition === def}
onClick={() => setDefinition(def)}
>
<strong>
{def.name}
</strong>
<div>
{values[def.id].indeterminate ?
<span style={{color: 'red'}}>Indeterminate</span> : values[def.id].values[0] ?? ""}
</div>
</ListItemButton>
</ListItem>
})}
</Box>

<Box sx={{
flexGrow: 1,
}}>
{value ? <>
<TextField
inputRef={inputRef}
autoFocus={true}
value={!value.indeterminate ? value.values[0] ?? '' : ''}
onChange={(e) => setValue(definition!.id, e.target.value)}
placeholder={value.indeterminate ? '-------' : undefined}
/>
</> : ''}
</Box>
</div>
}
Loading

0 comments on commit b7e1e25

Please sign in to comment.