Adds idea submission capability gated by a per-tenant feature flag. Super admins can enable/disable ideation for specific tenants via the admin tenant detail drawer. Users see a lightbulb icon in the header when enabled, opening a modal to submit ideas (title + description). Ideas are stored in shared schema for cross-tenant backlog querying. - Database: shared.ideas table (018-ideas.sql migration) - Backend: Ideas NestJS module (entity, service, controller) - Admin API: GET /admin/ideas, PUT /admin/ideas/:id/status, PUT /admin/organizations/:id/settings - Frontend: IdeaModal component, lightbulb ActionIcon in header - Admin UI: Feature Toggles card with ideation Switch in drawer Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
70 lines
1.9 KiB
TypeScript
70 lines
1.9 KiB
TypeScript
import { useState } from 'react';
|
|
import { Modal, TextInput, Textarea, Button, Stack } from '@mantine/core';
|
|
import { notifications } from '@mantine/notifications';
|
|
import { useMutation } from '@tanstack/react-query';
|
|
import api from '../../services/api';
|
|
|
|
interface IdeaModalProps {
|
|
opened: boolean;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export function IdeaModal({ opened, onClose }: IdeaModalProps) {
|
|
const [title, setTitle] = useState('');
|
|
const [description, setDescription] = useState('');
|
|
|
|
const submitIdea = useMutation({
|
|
mutationFn: async () => {
|
|
const { data } = await api.post('/ideas', { title, description });
|
|
return data;
|
|
},
|
|
onSuccess: () => {
|
|
notifications.show({ message: 'Idea submitted — thank you!', color: 'green' });
|
|
setTitle('');
|
|
setDescription('');
|
|
onClose();
|
|
},
|
|
onError: (err: any) => {
|
|
notifications.show({
|
|
message: err.response?.data?.message || 'Failed to submit idea',
|
|
color: 'red',
|
|
});
|
|
},
|
|
});
|
|
|
|
const handleClose = () => {
|
|
setTitle('');
|
|
setDescription('');
|
|
onClose();
|
|
};
|
|
|
|
return (
|
|
<Modal opened={opened} onClose={handleClose} title="Submit an Idea" size="md">
|
|
<Stack>
|
|
<TextInput
|
|
label="Title"
|
|
placeholder="Brief summary of your idea"
|
|
required
|
|
value={title}
|
|
onChange={(e) => setTitle(e.currentTarget.value)}
|
|
maxLength={255}
|
|
/>
|
|
<Textarea
|
|
label="Description"
|
|
placeholder="Describe your idea in more detail (optional)"
|
|
minRows={4}
|
|
value={description}
|
|
onChange={(e) => setDescription(e.currentTarget.value)}
|
|
/>
|
|
<Button
|
|
onClick={() => submitIdea.mutate()}
|
|
loading={submitIdea.isPending}
|
|
disabled={!title.trim()}
|
|
>
|
|
Submit Idea
|
|
</Button>
|
|
</Stack>
|
|
</Modal>
|
|
);
|
|
}
|