Skip to content

Commit eff9a0c

Browse files
committed
chore(misc): remaining component and utility updates
- Update App.tsx and routing configuration - Update common components (Chat, DocumentOverview, ImageDisplay, etc.) - Update hooks (useAuth, useAutocomplete, useBetaFeatures, etc.) - Update notebook, search, pages, templates, wolke features - Update GrueneJugendGenerator and homepage components - Update index.tsx entry point
1 parent 2009624 commit eff9a0c

47 files changed

Lines changed: 275 additions & 238 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/web/src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
2626
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
2727
const PopupNutzungsbedingungen = lazy(() => import('./components/Popups/popup_nutzungsbedingungen'));
2828
// const CustomGrueneratorenPopup = lazy(() => import('./components/Popups/popup_custom_grueneratoren'));
29-
const PopupWeihnachten = lazy(() => import('./components/Popups/popup_weihnachten'));
29+
const PopupAustriaLaunch = lazy(() => import('./components/Popups/popup_austria_launch'));
3030

3131
// QueryClient Instanz erstellen
3232
const queryClient = new QueryClient({
@@ -145,7 +145,7 @@ function App() {
145145
<RouteLogger />
146146
<SuspenseWrapper>
147147
<PopupNutzungsbedingungen />
148-
<PopupWeihnachten />
148+
<PopupAustriaLaunch />
149149
<div id="aria-live-region" aria-live="polite" className="sr-only"></div>
150150

151151
<Routes>

apps/web/src/components/common/Chat/ChatUI.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@
511511
padding: 0;
512512
background: var(--background-color-alt);
513513
color: var(--font-color);
514-
border: 1px solid var(--border-subtle);
514+
border: var(--border-subtle);
515515
border-radius: 50%;
516516
cursor: pointer;
517517
font-size: 14px;
@@ -665,7 +665,7 @@
665665
width: 28px;
666666
height: 28px;
667667
border-radius: 50%;
668-
border: 1px solid var(--border-subtle);
668+
border: var(--border-subtle);
669669
cursor: pointer;
670670
padding: 0;
671671
flex-shrink: 0;

apps/web/src/components/common/Chat/ChatUI.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ const ChatUI = ({ messages = [],
103103
email: user?.email
104104
});
105105
return props;
106-
}, [avatarRobotId, displayName, user?.email, profile]);
106+
}, [avatarRobotId, displayName, user?.email]);
107107

108108
// Use internal hook only if voice props not provided from parent
109109
const hasExternalVoice = externalStartRecording !== undefined;
@@ -195,8 +195,9 @@ const ChatUI = ({ messages = [],
195195

196196
<Suspense fallback={<div>Loading...</div>}><ReactMarkdown
197197
components={{
198-
// eslint-disable-next-line react/prop-types
199-
a: ({node, ...props}) => <a {...props} target="_blank" rel="noopener noreferrer" />
198+
a: ({ ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement>) => (
199+
<a {...props} target="_blank" rel="noopener noreferrer" />
200+
)
200201
}}
201202
>
202203
{msg.content}

apps/web/src/components/common/DocumentOverview.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,16 +484,44 @@ const DocumentOverview = ({
484484

485485
// Build action items via builder, falling back to custom actionItems if provided
486486

487+
// Helper to get file extension from filename
488+
const getFileExtension = (filename?: string) => {
489+
if (!filename) return null;
490+
const ext = filename.split('.').pop()?.toUpperCase();
491+
return ext && ext.length <= 4 ? ext : null;
492+
};
493+
487494
// Render default card
488495
const renderDefaultCard = (item: DocumentItem) => {
489496
const itemTitle = itemType === 'notebook' ? item.name : item.title;
490497
const isDocument = itemType === 'document';
498+
const fileExt = isDocument ? getFileExtension(item.title || '') : null;
491499

492500
return (
493501
<div
494502
key={item.id}
495503
className="document-card"
496504
>
505+
{/* Source Type Badge */}
506+
{isDocument && item.source_type && (
507+
<span className={`document-source-badge document-source-badge--${item.source_type}`}>
508+
{item.source_type === 'wolke' ? '☁️ Wolke' :
509+
item.source_type === 'url' ? '🔗 URL' : '📁 Upload'}
510+
</span>
511+
)}
512+
513+
{/* File Type Badge */}
514+
{fileExt && (
515+
<span className="document-file-badge">{fileExt}</span>
516+
)}
517+
518+
{/* Document Icon */}
519+
{isDocument && !item.preview_image_url && !item.thumbnail_url && (
520+
<div className="document-card-icon">
521+
<HiOutlineDocumentText />
522+
</div>
523+
)}
524+
497525
{/* Header with title and dropdown menu */}
498526
<div className="document-card-header">
499527
{/* Bulk selection checkbox */}

apps/web/src/components/common/EnhancedSelect/EnhancedSelect.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { lazy, Suspense, useCallback, memo, forwardRef, ComponentType, ReactNode } from 'react';
1+
import { lazy, Suspense, useCallback, memo, ComponentType, ReactNode, forwardRef, useRef, useImperativeHandle } from 'react';
22
import type { Props as ReactSelectProps, GroupBase, StylesConfig, MultiValueProps, PlaceholderProps } from 'react-select';
33
const Select = lazy(() => import('react-select')) as unknown as typeof import('react-select').default;
44
const CreatableSelect = lazy(() => import('react-select/creatable')) as unknown as typeof import('react-select/creatable').default;
@@ -59,6 +59,11 @@ interface IconConfig {
5959
[key: string]: unknown;
6060
}
6161

62+
interface EnhancedSelectRef {
63+
inputRef?: { blur: () => void } | null;
64+
[key: string]: unknown;
65+
}
66+
6267
interface EnhancedSelectProps extends Omit<ReactSelectProps<EnhancedSelectOption, boolean, GroupBase<EnhancedSelectOption>>, 'options' | 'formatOptionLabel'> {
6368
// Enhanced functionality
6469
enableTags?: boolean;
@@ -84,7 +89,7 @@ interface EnhancedSelectProps extends Omit<ReactSelectProps<EnhancedSelectOption
8489
components?: Record<string, ComponentType<unknown>>;
8590
}
8691

87-
const EnhancedSelect = forwardRef<HTMLDivElement, EnhancedSelectProps>(({
92+
const EnhancedSelect = forwardRef<EnhancedSelectRef, EnhancedSelectProps>(({
8893
// Enhanced functionality props
8994
enableTags = false,
9095
enableIcons = false,
@@ -111,7 +116,14 @@ const EnhancedSelect = forwardRef<HTMLDivElement, EnhancedSelectProps>(({
111116
styles: customStyles,
112117
inputId,
113118
...selectProps
114-
}, ref) => {
119+
}, forwardedRef) => {
120+
// Ref to the internal select component
121+
const selectRef = useRef<{ inputRef?: { blur: () => void } | null }>(null);
122+
123+
// Expose select API through imperative handle
124+
useImperativeHandle(forwardedRef, () => ({
125+
inputRef: selectRef.current?.inputRef
126+
}), []);
115127

116128
// Internal formatOptionLabel that handles enhanced features
117129
const internalFormatOptionLabel = useCallback((option: EnhancedSelectOption, { context }: { context: 'menu' | 'value' }) => {
@@ -227,7 +239,7 @@ const EnhancedSelect = forwardRef<HTMLDivElement, EnhancedSelectProps>(({
227239
...enhancedStyles,
228240
...customStyles,
229241
// Merge functions for overlapping style keys
230-
...(customStyles && Object.keys(customStyles).reduce<Record<string, unknown>>((acc, key) => {
242+
...(customStyles ? Object.keys(customStyles).reduce<Record<string, unknown>>((acc, key) => {
231243
const styleKey = key as keyof StylesConfig<EnhancedSelectOption, boolean, GroupBase<EnhancedSelectOption>>;
232244
const enhancedStyleFn = enhancedStyles[styleKey];
233245
const customStyleFn = customStyles[styleKey];
@@ -238,13 +250,13 @@ const EnhancedSelect = forwardRef<HTMLDivElement, EnhancedSelectProps>(({
238250
};
239251
}
240252
return acc;
241-
}, {}))
253+
}, {}) : {})
242254
};
243255

244256
const selectElement = (
245257
<Suspense fallback={<div>Loading...</div>}>
246258
<SelectComponent
247-
ref={ref}
259+
ref={selectRef as any}
248260
options={options}
249261
formatOptionLabel={internalFormatOptionLabel}
250262
components={components}

apps/web/src/components/common/ImageDisplay.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import CopyButton from './CopyButton';
55
import HelpTooltip from './HelpTooltip';
66
import { ProfileIconButton, ProfileActionButton } from '../profile/actions/ProfileActionButton';
77
import useAltTextGeneration from '../hooks/useAltTextGeneration';
8-
import useSharepicStore from '../../stores/sharepicStore';
8+
import { useAltTextStore } from '../../features/image-studio/hooks/useAltText';
99
import apiClient from '../utils/apiClient';
1010
import CanvaTemplateModal from './CanvaTemplateModal';
1111
import SharepicShareModal from './SharepicShareModal';
@@ -104,7 +104,7 @@ const ImageDisplay = ({ sharepicData,
104104
setAltTextLoading,
105105
setAltTextError,
106106
setShowAltText
107-
} = useSharepicStore();
107+
} = useAltTextStore();
108108

109109
if (!sharepicItems.length || !sharepicItems.some(item => item?.image)) {
110110
return null;
@@ -150,9 +150,10 @@ const ImageDisplay = ({ sharepicData,
150150
const imageBase64 = (currentSharepic.image || '').replace(/^data:image\/[^;]+;base64,/, '');
151151

152152
// Generate alt text using the existing hook
153-
const response = await generateAltTextForImage(imageBase64, currentSharepic.text || null);
153+
const textInput = typeof currentSharepic.text === 'string' ? currentSharepic.text : null;
154+
const response = await generateAltTextForImage(imageBase64, textInput);
154155

155-
if (response?.altText) {
156+
if (response && typeof response === 'object' && 'altText' in response && typeof response.altText === 'string') {
156157
setAltText(response.altText);
157158
setShowAltText(true);
158159
} else {

apps/web/src/components/common/InstructionFields/InstructionsGrid.tsx

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -42,34 +42,6 @@ const INSTRUCTION_FIELDS: InstructionField[] = [
4242
title: 'Presse & Social Media',
4343
placeholder: 'Gib hier deine Anweisungen für die Erstellung von Presse- und Social Media-Inhalten ein...',
4444
helpText: 'z.B. Tonalität, Hashtag-Präferenzen, Zielgruppen-Ansprache'
45-
},
46-
{
47-
name: 'customUniversalPrompt',
48-
dataKey: 'universalPrompt',
49-
title: 'Universelle Texte',
50-
placeholder: 'Gib hier deine Anweisungen für die Erstellung von universellen Texten ein...',
51-
helpText: 'z.B. allgemeine Schreibweise, politische Grundhaltung, Formulierungspräferenzen'
52-
},
53-
{
54-
name: 'customRedePrompt',
55-
dataKey: 'redePrompt',
56-
title: 'Reden',
57-
placeholder: 'Gib hier deine Anweisungen für die Erstellung von Reden ein...',
58-
helpText: 'z.B. bevorzugter Redestil, rhetorische Mittel, Ansprache der Zielgruppe'
59-
},
60-
{
61-
name: 'customBuergeranfragenPrompt',
62-
dataKey: 'buergeranfragenPrompt',
63-
title: 'Bürger*innenanfragen',
64-
placeholder: 'Gib hier deine Anweisungen für die Beantwortung von Bürger*innenanfragen ein...',
65-
helpText: 'z.B. bevorzugte Tonalität, Detailgrad, Ansprechpartner-Informationen'
66-
},
67-
{
68-
name: 'customGruenejugendPrompt',
69-
dataKey: 'gruenejugendPrompt',
70-
title: 'Grüne Jugend',
71-
placeholder: 'Gib hier deine Anweisungen für die Erstellung von Grüne Jugend-Inhalten ein...',
72-
helpText: 'z.B. jugendgerechte Sprache, spezielle Themen, Aktivismus-Fokus'
7345
}
7446
];
7547

@@ -103,12 +75,13 @@ const InstructionsGrid = ({
10375

10476
const getFieldValue = (fieldName: string): string => {
10577
const field = INSTRUCTION_FIELDS.find(f => f.name === fieldName);
106-
return data[field?.dataKey as string] || '';
78+
const value = data[field?.dataKey as string];
79+
return typeof value === 'string' ? value : '';
10780
};
10881

10982
const fieldsWithContent = INSTRUCTION_FIELDS.filter(field => {
11083
const value = data[field.dataKey];
111-
return value && value.trim().length > 0;
84+
return typeof value === 'string' && value.trim().length > 0;
11285
}).map(f => f.name);
11386

11487
const activeFieldNames = [...new Set([...fieldsWithContent, ...enabledFields])];

apps/web/src/components/common/Layout/PageLayout.tsx

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { JSX, useEffect, Suspense, lazy, useState, ReactNode } from 'react';
2-
import Header from '../../layout/Header/Header';
2+
import { Sidebar } from '../../layout/Sidebar';
3+
import SidebarToggle from '../../layout/SidebarToggle';
4+
import ProfileButton from '../../layout/Header/ProfileButton';
35
import { isDesktopApp } from '../../../utils/platform';
6+
import useSidebarStore from '../../../stores/sidebarStore';
7+
import '../../../assets/styles/components/layout/header.css';
48

5-
// Lazy load Footer
69
const Footer = lazy(() => import('../../layout/Footer/Footer'));
710

8-
// Lazy load desktop-specific components
911
const DesktopTitlebar = lazy(() => import('../../layout/DesktopTitlebar/DesktopTitlebar'));
1012
const DesktopSidebar = lazy(() => import('../../layout/DesktopSidebar/DesktopSidebar'));
1113
const UpdateNotification = lazy(() => import('../../desktop/UpdateNotification/UpdateNotification'));
@@ -19,12 +21,13 @@ interface PageLayoutProps {
1921

2022
const PageLayout = ({ children, darkMode, toggleDarkMode, showHeaderFooter = true }: PageLayoutProps): JSX.Element => {
2123
const [showFooter, setShowFooter] = useState(false);
24+
const sidebarOpen = useSidebarStore((state) => state.isOpen);
25+
const hideAppSidebar = useSidebarStore((state) => state.hideAppSidebar);
2226

2327
useEffect(() => {
2428
}, [showHeaderFooter, darkMode, children]);
2529

2630
useEffect(() => {
27-
// Verzögere das Anzeigen des Footers
2831
const footerTimeout = setTimeout(() => {
2932
setShowFooter(true);
3033
}, 1000);
@@ -38,7 +41,6 @@ const PageLayout = ({ children, darkMode, toggleDarkMode, showHeaderFooter = tru
3841

3942
const isDesktop = isDesktopApp();
4043

41-
// Desktop layout with titlebar and sidebar
4244
if (isDesktop) {
4345
return (
4446
<div className="desktop-layout">
@@ -58,17 +60,28 @@ const PageLayout = ({ children, darkMode, toggleDarkMode, showHeaderFooter = tru
5860
);
5961
}
6062

61-
// Web layout with header and footer
63+
const layoutClasses = [
64+
'app-layout',
65+
sidebarOpen ? 'sidebar-open' : '',
66+
hideAppSidebar ? 'sidebar-hidden' : '',
67+
].filter(Boolean).join(' ');
68+
6269
return (
63-
<>
64-
<Header />
65-
<main className="content-wrapper">{children}</main>
66-
{showFooter && (
67-
<Suspense fallback={<div style={{ height: '80px' }} />}>
68-
<Footer />
69-
</Suspense>
70-
)}
71-
</>
70+
<div className={layoutClasses}>
71+
<SidebarToggle />
72+
<div className="header-actions">
73+
<ProfileButton />
74+
</div>
75+
<Sidebar />
76+
<div className="app-content">
77+
<main className="content-wrapper">{children}</main>
78+
{showFooter && (
79+
<Suspense fallback={<div style={{ height: '80px' }} />}>
80+
<Footer />
81+
</Suspense>
82+
)}
83+
</div>
84+
</div>
7285
);
7386
};
7487

apps/web/src/components/common/SubtitleSegmentList.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ const SubtitleSegmentList = ({ segments,
4141
const activeSegmentId = getActiveSegmentId();
4242

4343
const handleSegmentClick = (segment: SubtitleSegmentListProps['segments'][number]) => {
44-
setEditingId(segment.id);
45-
onSegmentClick?.(segment.id);
44+
if (segment.id !== undefined) {
45+
setEditingId(segment.id);
46+
onSegmentClick?.(segment.id);
47+
}
4648
onSeek?.(segment.startTime || 0);
4749
};
4850

apps/web/src/components/common/TemplateLinkModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ const TemplateLinkModal = ({ template, onClose, onSubmit }: TemplateLinkModalPro
6969

7070
<div className="citation-modal-content">
7171
<div className="template-link-info">
72-
<p><strong>Server Template:</strong> {template.title}</p>
72+
<p><strong>Server Template:</strong> {typeof template.title === 'string' ? template.title : String(template.title)}</p>
7373
<p>Geben Sie Ihre eigene Canva URL ein, um diese mit dem Template zu verknüpfen:</p>
7474
</div>
7575

0 commit comments

Comments
 (0)