Phase 3: Optimize & clean up — unified projects, account enhancements, new tenant fix
- Unify reserve_components + capital_projects into single projects model with full CRUD backend and new Projects page frontend - Rewrite Capital Planning to read from unified projects/planning endpoint; add empty state directing users to Projects page when no planning items exist - Add default designation to assessment groups with auto-set on first creation; units now require an assessment group (pre-populated with default) - Add primary account designation (one per fund type) and balance adjustment via journal entries against equity offset accounts (3000/3100) - Add computed investment fields (interest earned, maturity value, days remaining) with PostgreSQL date arithmetic fix for DATE - DATE integer result - Restructure sidebar: investments in Accounts tab, Year-End under Reports, Planning section with Projects and Capital Planning - Fix new tenant creation seeding unwanted default chart of accounts — new tenants now start with a blank slate Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Title, Text, Card, Table, SimpleGrid, Group, Stack, Badge, Loader, Center,
|
||||
ThemeIcon, Button, Modal, TextInput, NumberInput, Textarea, Select, ActionIcon,
|
||||
ThemeIcon, Button, Modal, TextInput, NumberInput, Textarea, Select, ActionIcon, Tooltip,
|
||||
} from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import {
|
||||
IconPlus, IconEdit, IconCategory, IconCash, IconHome, IconArchive,
|
||||
IconPlus, IconEdit, IconCategory, IconCash, IconHome, IconArchive, IconStarFilled, IconStar,
|
||||
} from '@tabler/icons-react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import api from '../../services/api';
|
||||
@@ -24,6 +24,7 @@ interface AssessmentGroup {
|
||||
monthly_operating_income: string;
|
||||
monthly_reserve_income: string;
|
||||
total_monthly_income: string;
|
||||
is_default: boolean;
|
||||
is_active: boolean;
|
||||
}
|
||||
|
||||
@@ -105,6 +106,17 @@ export function AssessmentGroupsPage() {
|
||||
},
|
||||
});
|
||||
|
||||
const setDefaultMutation = useMutation({
|
||||
mutationFn: (id: string) => api.put(`/assessment-groups/${id}/set-default`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['assessment-groups'] });
|
||||
notifications.show({ message: 'Default group updated', color: 'green' });
|
||||
},
|
||||
onError: (err: any) => {
|
||||
notifications.show({ message: err.response?.data?.message || 'Error', color: 'red' });
|
||||
},
|
||||
});
|
||||
|
||||
const handleEdit = (group: AssessmentGroup) => {
|
||||
setEditing(group);
|
||||
form.setValues({
|
||||
@@ -223,10 +235,17 @@ export function AssessmentGroupsPage() {
|
||||
{groups.map((g) => (
|
||||
<Table.Tr key={g.id} style={{ opacity: g.is_active ? 1 : 0.5 }}>
|
||||
<Table.Td>
|
||||
<div>
|
||||
<Text fw={500}>{g.name}</Text>
|
||||
{g.description && <Text size="xs" c="dimmed">{g.description}</Text>}
|
||||
</div>
|
||||
<Group gap={8}>
|
||||
<div>
|
||||
<Group gap={6}>
|
||||
<Text fw={500}>{g.name}</Text>
|
||||
{g.is_default && (
|
||||
<Badge color="yellow" variant="light" size="xs">Default</Badge>
|
||||
)}
|
||||
</Group>
|
||||
{g.description && <Text size="xs" c="dimmed">{g.description}</Text>}
|
||||
</div>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
<Table.Td ta="center">
|
||||
<Badge variant="light">{g.actual_unit_count || g.unit_count}</Badge>
|
||||
@@ -256,6 +275,16 @@ export function AssessmentGroupsPage() {
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Group gap={4}>
|
||||
<Tooltip label={g.is_default ? 'Default group' : 'Set as default'}>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
color={g.is_default ? 'yellow' : 'gray'}
|
||||
onClick={() => !g.is_default && setDefaultMutation.mutate(g.id)}
|
||||
disabled={g.is_default}
|
||||
>
|
||||
{g.is_default ? <IconStarFilled size={16} /> : <IconStar size={16} />}
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<ActionIcon variant="subtle" onClick={() => handleEdit(g)}>
|
||||
<IconEdit size={16} />
|
||||
</ActionIcon>
|
||||
|
||||
Reference in New Issue
Block a user