u
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import { resolve, dirname } from 'node:path';
|
import { resolve, dirname } from 'node:path';
|
||||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import decodeAvif, { init as initAvifDecode } from '@jsquash/avif/decode.js';
|
import decodeAvif, { init as initAvifDecode } from '@jsquash/avif/decode.js';
|
||||||
import encodeAvif, { init as initAvifEncode } from '@jsquash/avif/encode.js';
|
import encodeAvif, { init as initAvifEncode } from '@jsquash/avif/encode.js';
|
||||||
import encodeJpeg, { init as initJpegEncode } from '@jsquash/jpeg/encode.js';
|
import encodeJpeg, { init as initJpegEncode } from '@jsquash/jpeg/encode.js';
|
||||||
|
|||||||
@@ -798,7 +798,7 @@ export class StorageManager extends ApiClient {
|
|||||||
return requireArray(data.orders, 'orders');
|
return requireArray(data.orders, 'orders');
|
||||||
}
|
}
|
||||||
|
|
||||||
async doSearch({ target, filter, offset, maxCount, onUpdate }) {
|
async doSearch({ target, onUpdate }) {
|
||||||
const articles = await this.articleIndexPromise;
|
const articles = await this.articleIndexPromise;
|
||||||
const query = target.trim().toLowerCase();
|
const query = target.trim().toLowerCase();
|
||||||
const matches = articles.filter(
|
const matches = articles.filter(
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import companyAutomat from './companyAutomat';
|
|||||||
import {
|
import {
|
||||||
parseCategoryTreeResponse,
|
parseCategoryTreeResponse,
|
||||||
normalizeCategoryNode,
|
normalizeCategoryNode,
|
||||||
getCategoryBreadcrumbs,
|
|
||||||
getCategoryChildren,
|
getCategoryChildren,
|
||||||
cleanCategorySlugPath,
|
cleanCategorySlugPath,
|
||||||
resolveCategoryIdFromSlugPath,
|
resolveCategoryIdFromSlugPath,
|
||||||
@@ -94,7 +93,7 @@ function refreshLoadedCategoryViewChildren(categories) {
|
|||||||
* Get or create a per-route category view automat for the given slug and categoryId.
|
* Get or create a per-route category view automat for the given slug and categoryId.
|
||||||
* (Backward compatibility helper)
|
* (Backward compatibility helper)
|
||||||
*/
|
*/
|
||||||
export function getOrCreateCategoryAutomat({ slugPath, categoryId }) {
|
export function getOrCreateCategoryAutomat({ slugPath }) {
|
||||||
const slug = cleanCategorySlugPath(slugPath);
|
const slug = cleanCategorySlugPath(slugPath);
|
||||||
return categoryViews.key(slug);
|
return categoryViews.key(slug);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import Box from '@mui/material/Box';
|
|||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import Tab from '@mui/material/Tab';
|
import Tab from '@mui/material/Tab';
|
||||||
import Tabs from '@mui/material/Tabs';
|
import Tabs from '@mui/material/Tabs';
|
||||||
import AccountTreeIcon from '@mui/icons-material/AccountTree';
|
|
||||||
import CategoryIcon from '@mui/icons-material/Category';
|
import CategoryIcon from '@mui/icons-material/Category';
|
||||||
import HistoryIcon from '@mui/icons-material/History';
|
import HistoryIcon from '@mui/icons-material/History';
|
||||||
import LabelIcon from '@mui/icons-material/Label';
|
import LabelIcon from '@mui/icons-material/Label';
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default class CategoryPickerDialog extends PureComponent {
|
|||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (this.props.categories === undefined || this.props.rootCategoryId === undefined) {
|
if (this.props.categories === undefined || this.props.rootCategoryId === undefined) {
|
||||||
this._unsub = categoryAutomat.subscribe((s) => {
|
this._unsub = categoryAutomat.subscribe((s) => {
|
||||||
this.setState((prev) => ({
|
this.setState(() => ({
|
||||||
categories: this.props.categories ?? s.categories,
|
categories: this.props.categories ?? s.categories,
|
||||||
rootCategoryId: this.props.rootCategoryId ?? s.rootCategoryId,
|
rootCategoryId: this.props.rootCategoryId ?? s.rootCategoryId,
|
||||||
}));
|
}));
|
||||||
@@ -132,7 +132,6 @@ export default class CategoryPickerDialog extends PureComponent {
|
|||||||
const dialogTitle = title || (mode === 'single' ? 'Kategorie auswählen' : 'Kategorie zuweisen');
|
const dialogTitle = title || (mode === 'single' ? 'Kategorie auswählen' : 'Kategorie zuweisen');
|
||||||
const actionLabel = confirmLabel || (mode === 'single' ? 'Auswählen' : 'Zuweisen');
|
const actionLabel = confirmLabel || (mode === 'single' ? 'Auswählen' : 'Zuweisen');
|
||||||
|
|
||||||
const selectedNode = categories && selectedId != null ? categories[String(selectedId)] : null;
|
|
||||||
const isAlreadyAssigned =
|
const isAlreadyAssigned =
|
||||||
mode !== 'single' && currentCategoryIds.some((id) => String(id) === String(selectedId));
|
mode !== 'single' && currentCategoryIds.some((id) => String(id) === String(selectedId));
|
||||||
const isSelectedDisabled =
|
const isSelectedDisabled =
|
||||||
|
|||||||
@@ -366,22 +366,20 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
const count = Math.max(1, Number(stuecklisteCount) || 1);
|
const count = Math.max(1, Number(stuecklisteCount) || 1);
|
||||||
|
|
||||||
const existingIndex = currentItems.findIndex((i) => i.sku === stuecklisteTargetArticle.sku);
|
const existingIndex = currentItems.findIndex((i) => i.sku === stuecklisteTargetArticle.sku);
|
||||||
let nextItems = [];
|
const nextItems =
|
||||||
if (existingIndex >= 0) {
|
existingIndex >= 0
|
||||||
nextItems = currentItems.map((item, idx) =>
|
? currentItems.map((item, idx) =>
|
||||||
idx === existingIndex ? { ...item, count: item.count + count } : item
|
idx === existingIndex ? { ...item, count: item.count + count } : item
|
||||||
);
|
)
|
||||||
} else {
|
: [
|
||||||
nextItems = [
|
...currentItems,
|
||||||
...currentItems,
|
{
|
||||||
{
|
articleId: stuecklisteTargetArticle.id,
|
||||||
articleId: stuecklisteTargetArticle.id,
|
sku: stuecklisteTargetArticle.sku,
|
||||||
sku: stuecklisteTargetArticle.sku,
|
count,
|
||||||
count,
|
sort: currentItems.length,
|
||||||
sort: currentItems.length,
|
},
|
||||||
},
|
];
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ busy: true });
|
this.setState({ busy: true });
|
||||||
try {
|
try {
|
||||||
@@ -492,25 +490,23 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
const count = Math.max(1, Number(optionCount) || 1);
|
const count = Math.max(1, Number(optionCount) || 1);
|
||||||
|
|
||||||
const existingIndex = currentOptions.findIndex((o) => o.sku === optionTargetArticle.sku);
|
const existingIndex = currentOptions.findIndex((o) => o.sku === optionTargetArticle.sku);
|
||||||
let nextOptions = [];
|
const nextOptions =
|
||||||
if (existingIndex >= 0) {
|
existingIndex >= 0
|
||||||
nextOptions = currentOptions.map((opt, idx) =>
|
? currentOptions.map((opt, idx) =>
|
||||||
idx === existingIndex
|
idx === existingIndex
|
||||||
? { ...opt, count: opt.count + count, mandated: Boolean(optionMandated) }
|
? { ...opt, count: opt.count + count, mandated: Boolean(optionMandated) }
|
||||||
: opt
|
: opt
|
||||||
);
|
)
|
||||||
} else {
|
: [
|
||||||
nextOptions = [
|
...currentOptions,
|
||||||
...currentOptions,
|
{
|
||||||
{
|
articleId: optionTargetArticle.id,
|
||||||
articleId: optionTargetArticle.id,
|
sku: optionTargetArticle.sku,
|
||||||
sku: optionTargetArticle.sku,
|
count,
|
||||||
count,
|
sort: currentOptions.length,
|
||||||
sort: currentOptions.length,
|
mandated: Boolean(optionMandated),
|
||||||
mandated: Boolean(optionMandated),
|
},
|
||||||
},
|
];
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ busy: true });
|
this.setState({ busy: true });
|
||||||
try {
|
try {
|
||||||
@@ -1363,15 +1359,9 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
busy,
|
busy,
|
||||||
activeCreatorMode,
|
activeCreatorMode,
|
||||||
activeListTab,
|
activeListTab,
|
||||||
selectedAttributeId,
|
|
||||||
selectedChildValueId,
|
selectedChildValueId,
|
||||||
newChildText,
|
newChildText,
|
||||||
isCustomChildValue,
|
isCustomChildValue,
|
||||||
stuecklisteTargetArticle,
|
|
||||||
stuecklisteCount,
|
|
||||||
optionTargetArticle,
|
|
||||||
optionCount,
|
|
||||||
optionMandated,
|
|
||||||
removeTargetChild,
|
removeTargetChild,
|
||||||
confirmClearType,
|
confirmClearType,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|||||||
@@ -184,13 +184,14 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let nextValues = [];
|
const nextValues = isExclusive
|
||||||
if (isExclusive) {
|
? [
|
||||||
const others = assignedAttributes.filter((a) => a.attributeId !== targetAttr.id);
|
...assignedAttributes
|
||||||
nextValues = [...others.map((a) => a.attributeValueId), targetValId];
|
.filter((a) => a.attributeId !== targetAttr.id)
|
||||||
} else {
|
.map((a) => a.attributeValueId),
|
||||||
nextValues = [...assignedAttributes.map((a) => a.attributeValueId), targetValId];
|
targetValId,
|
||||||
}
|
]
|
||||||
|
: [...assignedAttributes.map((a) => a.attributeValueId), targetValId];
|
||||||
|
|
||||||
this.setState({ saving: true });
|
this.setState({ saving: true });
|
||||||
try {
|
try {
|
||||||
@@ -222,19 +223,13 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let nextProps = [];
|
const sourceProps = isExclusive
|
||||||
if (isExclusive) {
|
? assignedProperties.filter((p) => p.propertyId !== propId)
|
||||||
const existing = assignedProperties.filter((p) => p.propertyId !== propId);
|
: assignedProperties;
|
||||||
nextProps = [
|
const nextProps = [
|
||||||
...existing.map((p) => ({ propertyId: p.propertyId, value: p.value, sort: p.sort })),
|
...sourceProps.map((p) => ({ propertyId: p.propertyId, value: p.value, sort: p.sort })),
|
||||||
{ propertyId: propId, value: val },
|
{ propertyId: propId, value: val },
|
||||||
];
|
];
|
||||||
} else {
|
|
||||||
nextProps = [
|
|
||||||
...assignedProperties.map((p) => ({ propertyId: p.propertyId, value: p.value, sort: p.sort })),
|
|
||||||
{ propertyId: propId, value: val },
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ saving: true });
|
this.setState({ saving: true });
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ function getMatchingAndAncestorIds(categories, query, rootCategoryId, excludeIds
|
|||||||
if (!categories || !query.trim()) return matches;
|
if (!categories || !query.trim()) return matches;
|
||||||
const q = query.trim().toLowerCase();
|
const q = query.trim().toLowerCase();
|
||||||
|
|
||||||
for (const [idStr, node] of Object.entries(categories)) {
|
for (const node of Object.values(categories)) {
|
||||||
if (excludeIds.some((id) => String(id) === String(node.id))) continue;
|
if (excludeIds.some((id) => String(id) === String(node.id))) continue;
|
||||||
const label = categoryDisplayName(node.name, node.id, rootCategoryId).toLowerCase();
|
const label = categoryDisplayName(node.name, node.id, rootCategoryId).toLowerCase();
|
||||||
if (label.includes(q)) {
|
if (label.includes(q)) {
|
||||||
@@ -580,7 +580,6 @@ export default class CategoryTree extends PureComponent {
|
|||||||
addCategoryName,
|
addCategoryName,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
renameDialogOpen,
|
renameDialogOpen,
|
||||||
renameNode,
|
|
||||||
renameValue,
|
renameValue,
|
||||||
deleteDialogOpen,
|
deleteDialogOpen,
|
||||||
deleteNode,
|
deleteNode,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
ROOT_CATEGORY_NAME,
|
ROOT_CATEGORY_NAME,
|
||||||
categorySlug,
|
categorySlug,
|
||||||
getCategoryBreadcrumbs,
|
getCategoryBreadcrumbs,
|
||||||
getCategoryChildren,
|
|
||||||
} from '../../utils/categoryUtils';
|
} from '../../utils/categoryUtils';
|
||||||
|
|
||||||
export { ROOT_CATEGORY_NAME, categorySlug };
|
export { ROOT_CATEGORY_NAME, categorySlug };
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import MenuItem from '@mui/material/MenuItem';
|
|||||||
import Select from '@mui/material/Select';
|
import Select from '@mui/material/Select';
|
||||||
import FormControl from '@mui/material/FormControl';
|
import FormControl from '@mui/material/FormControl';
|
||||||
import InputLabel from '@mui/material/InputLabel';
|
import InputLabel from '@mui/material/InputLabel';
|
||||||
import Chip from '@mui/material/Chip';
|
|
||||||
import CircularProgress from '@mui/material/CircularProgress';
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
|
import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
|
||||||
import SendRoundedIcon from '@mui/icons-material/SendRounded';
|
import SendRoundedIcon from '@mui/icons-material/SendRounded';
|
||||||
@@ -231,7 +230,6 @@ export default class ChatDrawer extends PureComponent {
|
|||||||
const {
|
const {
|
||||||
conversations,
|
conversations,
|
||||||
selectedConversationId,
|
selectedConversationId,
|
||||||
sseStatus,
|
|
||||||
loadingConversations,
|
loadingConversations,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
@@ -249,21 +247,8 @@ export default class ChatDrawer extends PureComponent {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.25 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
<Box
|
<AutoAwesomeRoundedIcon sx={{ fontSize: 22, color: 'primary.main' }} />
|
||||||
sx={{
|
|
||||||
width: 32,
|
|
||||||
height: 32,
|
|
||||||
borderRadius: 2,
|
|
||||||
bgcolor: 'primary.main',
|
|
||||||
color: 'primary.contrastText',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AutoAwesomeRoundedIcon sx={{ fontSize: 18 }} />
|
|
||||||
</Box>
|
|
||||||
<Typography variant="subtitle1" fontWeight={700}>
|
<Typography variant="subtitle1" fontWeight={700}>
|
||||||
KI-Assistent
|
KI-Assistent
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -275,80 +260,82 @@ export default class ChatDrawer extends PureComponent {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Conversation Selector Bar */}
|
{/* Conversation Selector Bar */}
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
{conversations.length > 0 && (
|
||||||
<FormControl fullWidth size="small">
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
<InputLabel id="chat-conversation-label">Konversation</InputLabel>
|
<FormControl fullWidth size="small">
|
||||||
<Select
|
<InputLabel id="chat-conversation-label">Konversation</InputLabel>
|
||||||
labelId="chat-conversation-label"
|
<Select
|
||||||
value={selectedConversationId || ''}
|
labelId="chat-conversation-label"
|
||||||
label="Konversation"
|
value={selectedConversationId || ''}
|
||||||
onChange={this.handleSelectConversation}
|
label="Konversation"
|
||||||
disabled={loadingConversations || conversations.length === 0}
|
onChange={this.handleSelectConversation}
|
||||||
sx={{ borderRadius: 2 }}
|
disabled={loadingConversations || conversations.length === 0}
|
||||||
renderValue={(selectedId) => {
|
sx={{ borderRadius: 2 }}
|
||||||
const conv = conversations.find((c) => c.id === selectedId);
|
renderValue={(selectedId) => {
|
||||||
if (!conv) return '';
|
const conv = conversations.find((c) => c.id === selectedId);
|
||||||
return (
|
if (!conv) return '';
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, overflow: 'hidden' }}>
|
return (
|
||||||
<Typography variant="body2" fontWeight={600} noWrap>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, overflow: 'hidden' }}>
|
||||||
#{conv.id}
|
<Typography variant="body2" fontWeight={600} noWrap>
|
||||||
</Typography>
|
#{conv.id}
|
||||||
<Typography variant="caption" color="text.secondary" noWrap>
|
</Typography>
|
||||||
{conv.model || 'Standard'}
|
<Typography variant="caption" color="text.secondary" noWrap>
|
||||||
</Typography>
|
{conv.model || 'Standard'}
|
||||||
</Box>
|
</Typography>
|
||||||
);
|
</Box>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{conversations.map((c) => (
|
||||||
|
<MenuItem key={c.id} value={c.id}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, width: '100%', overflow: 'hidden' }}>
|
||||||
|
<Typography variant="body2" fontWeight={600} noWrap>
|
||||||
|
#{c.id}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="caption" color="text.secondary" noWrap sx={{ flexGrow: 1 }}>
|
||||||
|
{c.model || 'Standard'}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
color="error"
|
||||||
|
disabled={!selectedConversationId}
|
||||||
|
onClick={(e) => this.handleDeleteConversation(e, selectedConversationId)}
|
||||||
|
aria-label="Konversation löschen"
|
||||||
|
sx={{
|
||||||
|
borderRadius: 2,
|
||||||
|
border: 1,
|
||||||
|
borderColor: 'divider',
|
||||||
|
p: 0.9,
|
||||||
|
flexShrink: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{conversations.map((c) => (
|
<DeleteOutlineRoundedIcon fontSize="small" />
|
||||||
<MenuItem key={c.id} value={c.id}>
|
</IconButton>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, width: '100%', overflow: 'hidden' }}>
|
|
||||||
<Typography variant="body2" fontWeight={600} noWrap>
|
|
||||||
#{c.id}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="caption" color="text.secondary" noWrap sx={{ flexGrow: 1 }}>
|
|
||||||
{c.model || 'Standard'}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
color="error"
|
color="primary"
|
||||||
disabled={!selectedConversationId}
|
disabled={!selectedConversationId}
|
||||||
onClick={(e) => this.handleDeleteConversation(e, selectedConversationId)}
|
onClick={this.handleNewChat}
|
||||||
aria-label="Konversation löschen"
|
aria-label="Neuer Chat"
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
border: 1,
|
border: 1,
|
||||||
borderColor: 'divider',
|
borderColor: 'divider',
|
||||||
p: 0.9,
|
p: 0.9,
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DeleteOutlineRoundedIcon fontSize="small" />
|
<AddRoundedIcon fontSize="small" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
</Box>
|
||||||
<IconButton
|
)}
|
||||||
size="small"
|
|
||||||
color="primary"
|
|
||||||
disabled={!selectedConversationId}
|
|
||||||
onClick={this.handleNewChat}
|
|
||||||
aria-label="Neuer Chat"
|
|
||||||
sx={{
|
|
||||||
borderRadius: 2,
|
|
||||||
border: 1,
|
|
||||||
borderColor: 'divider',
|
|
||||||
p: 0.9,
|
|
||||||
flexShrink: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AddRoundedIcon fontSize="small" />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -362,7 +349,6 @@ export default class ChatDrawer extends PureComponent {
|
|||||||
messages,
|
messages,
|
||||||
streamingText,
|
streamingText,
|
||||||
loadingConversations,
|
loadingConversations,
|
||||||
loadingMessages,
|
|
||||||
model,
|
model,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
@@ -391,20 +377,7 @@ export default class ChatDrawer extends PureComponent {
|
|||||||
gap: 2,
|
gap: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<AutoAwesomeRoundedIcon sx={{ fontSize: 44, color: 'primary.main', opacity: 0.85 }} />
|
||||||
sx={{
|
|
||||||
width: 56,
|
|
||||||
height: 56,
|
|
||||||
borderRadius: '50%',
|
|
||||||
bgcolor: 'primary.light',
|
|
||||||
color: 'primary.main',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AutoAwesomeRoundedIcon sx={{ fontSize: 32 }} />
|
|
||||||
</Box>
|
|
||||||
<Typography variant="h6" fontWeight={700}>
|
<Typography variant="h6" fontWeight={700}>
|
||||||
Willkommen im Chat
|
Willkommen im Chat
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -177,14 +177,17 @@ function MessageRow({ msg, showTime = true }) {
|
|||||||
bodyLength = textContent.length;
|
bodyLength = textContent.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
let displayText = '';
|
const isToolAnswer =
|
||||||
if (resObj && ('answer' in resObj || 'content' in resObj) && ('tool_call_id' in resObj) && !('status' in resObj)) {
|
Boolean(resObj) &&
|
||||||
displayText = resObj.answer || resObj.content || '';
|
('answer' in resObj || 'content' in resObj) &&
|
||||||
} else {
|
'tool_call_id' in resObj &&
|
||||||
displayText = statusText
|
!('status' in resObj);
|
||||||
|
|
||||||
|
const displayText = isToolAnswer
|
||||||
|
? resObj.answer || resObj.content || ''
|
||||||
|
: statusText
|
||||||
? `${statusText}${bodyLength != null ? ` · ${bodyLength} byte` : ''}`
|
? `${statusText}${bodyLength != null ? ` · ${bodyLength} byte` : ''}`
|
||||||
: (bodyLength != null ? `${bodyLength} byte` : String(textContent));
|
: (bodyLength != null ? `${bodyLength} byte` : String(textContent));
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'flex-start', py: 0.35, px: 2 }}>
|
<Box sx={{ display: 'flex', justifyContent: 'flex-start', py: 0.35, px: 2 }}>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export class Attachment extends PureComponent {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleTileClick = (e) => {
|
handleTileClick = () => {
|
||||||
const { attachment, onClick } = this.props;
|
const { attachment, onClick } = this.props;
|
||||||
if (onClick && attachment) {
|
if (onClick && attachment) {
|
||||||
onClick(attachment);
|
onClick(attachment);
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Button from '@mui/material/Button';
|
|
||||||
import CircularProgress from '@mui/material/CircularProgress';
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
import { enqueueSnackbar } from 'notistack';
|
import { enqueueSnackbar } from 'notistack';
|
||||||
import BackendError from '../BackendError';
|
import BackendError from '../BackendError';
|
||||||
import { PageTitleRight } from '../PageLayout';
|
import { PanelTileGrid } from 'dashgui/panels';
|
||||||
import { PanelTile, PanelTileGrid } from 'dashgui/panels';
|
|
||||||
import { StorageManagerContext } from '../../api/storageManagerContext';
|
import { StorageManagerContext } from '../../api/storageManagerContext';
|
||||||
import { requireArray, requireDefined, requireObject } from '../../utils/apiShape';
|
import { requireArray, requireDefined, requireObject } from '../../utils/apiShape';
|
||||||
import { backendErrorFrom } from '../../utils/backendError';
|
import { backendErrorFrom } from '../../utils/backendError';
|
||||||
@@ -435,7 +432,7 @@ class ManifestForm extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { kind, mode } = this.props;
|
const { kind } = this.props;
|
||||||
const cfg = this.cfg;
|
const cfg = this.cfg;
|
||||||
const {
|
const {
|
||||||
status,
|
status,
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ class ManifestHeader extends PureComponent {
|
|||||||
saving,
|
saving,
|
||||||
pdfUploading,
|
pdfUploading,
|
||||||
attachments,
|
attachments,
|
||||||
lines,
|
|
||||||
onPdfUpload,
|
onPdfUpload,
|
||||||
onPdfRescan,
|
onPdfRescan,
|
||||||
onPdfDelete,
|
onPdfDelete,
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ class ManifestLines extends PureComponent {
|
|||||||
onAddLine,
|
onAddLine,
|
||||||
onRemoveLine,
|
onRemoveLine,
|
||||||
onRemoveAllLines,
|
onRemoveAllLines,
|
||||||
onSave,
|
|
||||||
columns = LINE_COLUMNS,
|
columns = LINE_COLUMNS,
|
||||||
supplierArticlesMap = {},
|
supplierArticlesMap = {},
|
||||||
supplierArticlesByLineKey = {},
|
supplierArticlesByLineKey = {},
|
||||||
|
|||||||
@@ -275,31 +275,6 @@ function fileToBase64(file) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert base64 string to Blob
|
|
||||||
* @param {string} base64
|
|
||||||
* @param {string} mimeType
|
|
||||||
* @returns {Blob}
|
|
||||||
*/
|
|
||||||
function base64ToBlob(base64, mimeType) {
|
|
||||||
const clean = base64.includes(',') ? base64.split(',')[1] : base64;
|
|
||||||
const byteCharacters = atob(clean);
|
|
||||||
const byteArrays = [];
|
|
||||||
const sliceSize = 512;
|
|
||||||
|
|
||||||
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
|
|
||||||
const slice = byteCharacters.slice(offset, offset + sliceSize);
|
|
||||||
const byteNumbers = new Array(slice.length);
|
|
||||||
for (let i = 0; i < slice.length; i++) {
|
|
||||||
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
||||||
}
|
|
||||||
const byteArray = new Uint8Array(byteNumbers);
|
|
||||||
byteArrays.push(byteArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Blob(byteArrays, { type: mimeType });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restore attachments from API response
|
* Restore attachments from API response
|
||||||
* @param {Array<{id: number, fileId?: number, manifestId?: number, mimetype: string, name: string, thumbavif?: any, size: number, created?: string, meta?: object}>} apiFiles
|
* @param {Array<{id: number, fileId?: number, manifestId?: number, mimetype: string, name: string, thumbavif?: any, size: number, created?: string, meta?: object}>} apiFiles
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ class KundeListePage extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { status, error, entities, groupsById, busyId } = this.state;
|
const { status, error, entities, busyId } = this.state;
|
||||||
|
|
||||||
if (status === 'failed') {
|
if (status === 'failed') {
|
||||||
return <BackendError error={error} onRetry={this.handleRetry} />;
|
return <BackendError error={error} onRetry={this.handleRetry} />;
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import { enqueueSnackbar } from 'notistack';
|
|||||||
import { PanelTile, PanelTileGrid } from 'dashgui/panels';
|
import { PanelTile, PanelTileGrid } from 'dashgui/panels';
|
||||||
import BackendError from '../../components/BackendError';
|
import BackendError from '../../components/BackendError';
|
||||||
import withNavigate from '../../components/withNavigate';
|
import withNavigate from '../../components/withNavigate';
|
||||||
import { PageTitleRight } from '../../components/PageLayout';
|
|
||||||
import {
|
import {
|
||||||
ManifestHeader,
|
ManifestHeader,
|
||||||
ManifestLines,
|
ManifestLines,
|
||||||
|
|||||||
@@ -16,10 +16,7 @@
|
|||||||
* only as a de-duplication / reference key, not as the SKU.
|
* only as a de-duplication / reference key, not as the SKU.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { parseImportCsv } from './importArtikelCsv';
|
||||||
parseImportCsv,
|
|
||||||
parseCategoryPath,
|
|
||||||
} from './importArtikelCsv';
|
|
||||||
import { article, category, brand } from '../api';
|
import { article, category, brand } from '../api';
|
||||||
import {
|
import {
|
||||||
requireObject,
|
requireObject,
|
||||||
@@ -27,7 +24,6 @@ import {
|
|||||||
} from './apiShape';
|
} from './apiShape';
|
||||||
|
|
||||||
function logImport(message) {
|
function logImport(message) {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(`[import] ${message}`);
|
console.log(`[import] ${message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user