no redux
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { useState, useRef } from 'react';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { Outlet, useLocation } from 'react-router-dom';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import Box from '@mui/material/Box';
|
||||
import Popover from '@mui/material/Popover';
|
||||
import MenuList from '@mui/material/MenuList';
|
||||
@@ -14,13 +13,7 @@ import { enqueueSnackbar } from 'notistack';
|
||||
import PageLayout from './PageLayout';
|
||||
import { NAV_ITEMS, SUB_NAV, resolveActiveSub } from '../nav';
|
||||
import { isKategorienPath } from '../pages/artikel/categoryRoute';
|
||||
import {
|
||||
selectRenameMode,
|
||||
setRenameMode,
|
||||
selectCategoryMap,
|
||||
selectCategoryRootId,
|
||||
loadCategoryTree,
|
||||
} from '../store/categorySlice';
|
||||
import categoryAutomat from '../automats/categoryAutomat';
|
||||
import { ActionElevatedPill, ToggleElevatedPill } from 'dashgui/ElevatedPill';
|
||||
import { article, category } from '../api';
|
||||
import { importArtikelCsv } from '../utils/importArtikel';
|
||||
@@ -37,15 +30,31 @@ function triggerDownload(blob, filename) {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function selectCatSlice(s) {
|
||||
return {
|
||||
renameMode: s.renameMode,
|
||||
categories: s.categories,
|
||||
rootCategoryId: s.rootCategoryId,
|
||||
};
|
||||
}
|
||||
|
||||
export default function SectionLayout({ sectionPath }) {
|
||||
const { pathname } = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const renameMode = useSelector(selectRenameMode);
|
||||
const categories = useSelector(selectCategoryMap);
|
||||
const rootCategoryId = useSelector(selectCategoryRootId);
|
||||
const importInputRef = useRef(null);
|
||||
const [extrasAnchor, setExtrasAnchor] = useState(null);
|
||||
const [importing, setImporting] = useState(false);
|
||||
const [catState, setCatState] = useState(() => selectCatSlice(categoryAutomat.state));
|
||||
|
||||
useEffect(() => {
|
||||
// Automat supports callback subscriptions: (state, partial) => void
|
||||
const unsub = categoryAutomat.subscribe((state) => {
|
||||
setCatState(selectCatSlice(state));
|
||||
}, selectCatSlice);
|
||||
return unsub;
|
||||
}, []);
|
||||
|
||||
const { renameMode, categories, rootCategoryId } = catState;
|
||||
|
||||
const title = NAV_ITEMS.find((item) => item.path === sectionPath)?.label ?? '';
|
||||
const subNav = SUB_NAV[sectionPath];
|
||||
const subSegment = resolveActiveSub(pathname, sectionPath);
|
||||
@@ -109,7 +118,7 @@ export default function SectionLayout({ sectionPath }) {
|
||||
},
|
||||
});
|
||||
if (result.ok > 0 && rootCategoryId != null) {
|
||||
await dispatch(loadCategoryTree()).unwrap();
|
||||
await categoryAutomat.actions.loadTree();
|
||||
}
|
||||
if (result.ok > 0) {
|
||||
enqueueSnackbar(
|
||||
@@ -136,7 +145,7 @@ export default function SectionLayout({ sectionPath }) {
|
||||
<ToggleElevatedPill
|
||||
label="Edit"
|
||||
checked={renameMode}
|
||||
onToggle={(checked) => dispatch(setRenameMode(checked))}
|
||||
onToggle={(checked) => categoryAutomat.actions.setRenameMode(checked)}
|
||||
/>
|
||||
<ActionElevatedPill
|
||||
label="Extras"
|
||||
|
||||
@@ -14,7 +14,7 @@ import CheckIcon from '@mui/icons-material/Check';
|
||||
import FolderIcon from '@mui/icons-material/Folder';
|
||||
import FolderOpenIcon from '@mui/icons-material/FolderOpen';
|
||||
import Box from '@mui/material/Box';
|
||||
import { getCategoryChildren, categoryDisplayName } from '../../store/categorySlice';
|
||||
import { getCategoryChildren, categoryDisplayName } from '../../utils/categoryUtils';
|
||||
function CategoryNode({
|
||||
categories,
|
||||
categoryId,
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { ActionTile } from '../tiles';
|
||||
import {
|
||||
createArticles as createArticlesThunk,
|
||||
createArticle as createArticleThunk,
|
||||
} from '../../store/categorySlice';
|
||||
import categoryAutomat from '../../automats/categoryAutomat';
|
||||
import ArticleNameDialog from './ArticleNameDialog';
|
||||
|
||||
class NewArticleTile extends Component {
|
||||
@@ -28,15 +24,16 @@ class NewArticleTile extends Component {
|
||||
const { parentId } = this.props;
|
||||
const list = (names ?? []).filter(Boolean);
|
||||
if (list.length <= 1) {
|
||||
const article = await this.props
|
||||
.createArticle({ parentId, name: list[0] ?? '' })
|
||||
.unwrap();
|
||||
const article = await categoryAutomat.actions.createArticle({
|
||||
parentId,
|
||||
name: list[0] ?? '',
|
||||
});
|
||||
if (article) {
|
||||
this.props.onOpenArticle?.(article);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const created = await this.props.createArticles({ parentId, names: list }).unwrap();
|
||||
const created = await categoryAutomat.actions.createArticles({ parentId, names: list });
|
||||
if (created?.[0]) {
|
||||
this.props.onOpenArticle?.(created[0]);
|
||||
}
|
||||
@@ -62,9 +59,4 @@ class NewArticleTile extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
createArticle: createArticleThunk,
|
||||
createArticles: createArticlesThunk,
|
||||
};
|
||||
|
||||
export default connect(null, mapDispatchToProps)(NewArticleTile);
|
||||
export default NewArticleTile;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Component, PureComponent } from 'react';
|
||||
import { EditablePanelTile } from 'dashgui/panels';
|
||||
import SingleLineTextEdit from 'dashgui/SingleLineTextEdit';
|
||||
import { StorageManagerContext } from '../../../api/storageManagerContext';
|
||||
import { getCategoryBreadcrumbs } from '../../../store/categorySlice';
|
||||
import { getCategoryBreadcrumbs } from '../../../utils/categoryUtils';
|
||||
import { Box, Chip, IconButton } from '@mui/material';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import CategoryPickerDialog from '../CategoryPickerDialog';
|
||||
|
||||
@@ -169,12 +169,7 @@ function priceDraftValid(draft) {
|
||||
|
||||
function companyFromStore(store) {
|
||||
if (!store) return null;
|
||||
const companies = store.companies;
|
||||
const session = store.authMe;
|
||||
if (companies == null || session == null) return null;
|
||||
const companyId = session.user?.selectedCompanyId;
|
||||
if (companyId == null) return null;
|
||||
return companies.find((c) => c.id === companyId) ?? null;
|
||||
return store.company ?? null;
|
||||
}
|
||||
|
||||
class PriceAmountField extends Component {
|
||||
@@ -395,18 +390,12 @@ export default class ArticleViewPrice extends PureComponent {
|
||||
async load() {
|
||||
const store = this.context;
|
||||
try {
|
||||
const [unitList, companies, session] = await Promise.all([
|
||||
const [unitList, company] = await Promise.all([
|
||||
store.unitPromise,
|
||||
store.companyPromise,
|
||||
store.authMePromise,
|
||||
]);
|
||||
const companyId = requireDefined(session.user?.selectedCompanyId, 'selectedCompanyId');
|
||||
const current = requireDefined(
|
||||
companies.find((c) => c.id === companyId),
|
||||
'company',
|
||||
);
|
||||
if (!this._cancelled) {
|
||||
this.setState({ units: unitList, company: current });
|
||||
this.setState({ units: unitList, company: company ?? null });
|
||||
}
|
||||
} catch (err) {
|
||||
if (!this._cancelled) notifyError(err);
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { ActionTile } from '../tiles';
|
||||
import {
|
||||
createCategories as createCategoriesThunk,
|
||||
createCategory as createCategoryThunk,
|
||||
} from '../../store/categorySlice';
|
||||
import categoryAutomat from '../../automats/categoryAutomat';
|
||||
import CategoryNameDialog from './CategoryNameDialog';
|
||||
|
||||
class NewCategoryTile extends Component {
|
||||
@@ -28,10 +24,10 @@ class NewCategoryTile extends Component {
|
||||
const { parentId } = this.props;
|
||||
const list = (names ?? []).filter(Boolean);
|
||||
if (list.length <= 1) {
|
||||
await this.props.createCategory({ parentId, name: list[0] ?? '' }).unwrap();
|
||||
await categoryAutomat.actions.createCategory({ parentId, name: list[0] ?? '' });
|
||||
return;
|
||||
}
|
||||
await this.props.createCategories({ parentId, names: list }).unwrap();
|
||||
await categoryAutomat.actions.createCategories({ parentId, names: list });
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -55,9 +51,4 @@ class NewCategoryTile extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
createCategory: createCategoryThunk,
|
||||
createCategories: createCategoriesThunk,
|
||||
};
|
||||
|
||||
export default connect(null, mapDispatchToProps)(NewCategoryTile);
|
||||
export default NewCategoryTile;
|
||||
|
||||
@@ -8,8 +8,43 @@ import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf';
|
||||
import SyncIcon from '@mui/icons-material/Sync';
|
||||
import DeleteOutlineOutlinedIcon from '@mui/icons-material/DeleteOutlineOutlined';
|
||||
import { tileBaseSx, TILE_SIZE } from 'dashgui/tiles';
|
||||
import { generateTextThumbnail } from '../../utils/textThumbnail';
|
||||
|
||||
export class Attachment extends PureComponent {
|
||||
state = {
|
||||
generatedThumbnail: null,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.ensureThumbnail();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.attachment !== prevProps.attachment) {
|
||||
this.ensureThumbnail();
|
||||
}
|
||||
}
|
||||
|
||||
async ensureThumbnail() {
|
||||
const { attachment } = this.props;
|
||||
if (!attachment || attachment.thumbnail) return;
|
||||
const isCsv = attachment.type === 'text/csv' || attachment.name?.toLowerCase().endsWith('.csv');
|
||||
const isText = attachment.type?.startsWith('text/') || attachment.name?.toLowerCase().endsWith('.txt') || isCsv;
|
||||
if (!isText) return;
|
||||
|
||||
const source = attachment.text || attachment.file;
|
||||
if (!source) return;
|
||||
|
||||
try {
|
||||
const thumb = await generateTextThumbnail(source);
|
||||
if (thumb && this.props.attachment === attachment) {
|
||||
this.setState({ generatedThumbnail: thumb });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Fehler beim Generieren des Text-Thumbnails:', err);
|
||||
}
|
||||
}
|
||||
|
||||
handleRescan = (e) => {
|
||||
e?.stopPropagation?.();
|
||||
const { attachment, onRescan } = this.props;
|
||||
@@ -56,7 +91,7 @@ export class Attachment extends PureComponent {
|
||||
|
||||
render() {
|
||||
const { attachment, disabled, sx } = this.props;
|
||||
const thumbnail = attachment?.thumbnail;
|
||||
const thumbnail = attachment?.thumbnail || this.state.generatedThumbnail;
|
||||
const name = attachment?.name || 'Dokument';
|
||||
const size = attachment?.size;
|
||||
const isPdf = attachment?.type === 'application/pdf' || name.toLowerCase().endsWith('.pdf');
|
||||
|
||||
@@ -341,10 +341,18 @@ export function collectFilesForUpload(attachments = [], { onlyNew = false } = {}
|
||||
}
|
||||
}
|
||||
|
||||
let thumbavif = null;
|
||||
if (attachment.thumbnail) {
|
||||
thumbavif = attachment.thumbnail.includes(',')
|
||||
? attachment.thumbnail.split(',')[1]
|
||||
: attachment.thumbnail;
|
||||
}
|
||||
|
||||
files.push({
|
||||
file: fileToUpload,
|
||||
name: attachment.name || fileToUpload.name,
|
||||
mimetype: attachment.type || fileToUpload.type,
|
||||
thumbavif,
|
||||
});
|
||||
}
|
||||
return files;
|
||||
@@ -389,6 +397,10 @@ export async function buildManifestBody(
|
||||
const filesToUpload = collectFilesForUpload(form.attachments, { onlyNew: isUpdate });
|
||||
if (filesToUpload.length > 0) {
|
||||
const formData = new FormData();
|
||||
body.filesMeta = filesToUpload.map((item) => ({
|
||||
name: item.name,
|
||||
thumbavif: item.thumbavif ?? null,
|
||||
}));
|
||||
formData.append('data', JSON.stringify(body));
|
||||
for (const item of filesToUpload) {
|
||||
formData.append('files', item.file, item.name);
|
||||
|
||||
Reference in New Issue
Block a user