feat: UX enhancements, member limits, forecast fix, and menu cleanup (v2026.3.19)
- Onboarding wizard: add Reserve Account step between Operating and Assessments, redirect to Budget Planning on completion - Dashboard: health score pending state shows clickable links to set up missing items - Projects/Vendors: rich empty-state wizard screens with real-world examples and CTAs - Investment Planning: auto-refresh AI recommendations when empty or stale (>30 days) - Hide Invoices and Payments menus (see PARKING-LOT.md for re-enablement) - Send welcome email via Resend when new members are added to a tenant - Enforce 5-member limit for Starter/Standard/Professional plans (Enterprise unlimited) - Cash flow forecast: only mark months as "Actual" when journal entries exist, fixing the issue where months without data showed as actuals - Bump version to 2026.3.19 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
62
frontend/src/pages/vendors/VendorsPage.tsx
vendored
62
frontend/src/pages/vendors/VendorsPage.tsx
vendored
@@ -1,13 +1,13 @@
|
||||
import { useState, useRef } from 'react';
|
||||
import {
|
||||
Title, Table, Group, Button, Stack, TextInput, Modal,
|
||||
Switch, Badge, ActionIcon, Text, Loader, Center,
|
||||
Switch, Badge, ActionIcon, Text, Loader, Center, Card, ThemeIcon, List,
|
||||
} from '@mantine/core';
|
||||
import { DateInput } from '@mantine/dates';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { IconPlus, IconEdit, IconSearch, IconUpload, IconDownload } from '@tabler/icons-react';
|
||||
import { IconPlus, IconEdit, IconSearch, IconUpload, IconDownload, IconUsers, IconBulb, IconRocket } from '@tabler/icons-react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import api from '../../services/api';
|
||||
import { useIsReadOnly } from '../../stores/authStore';
|
||||
@@ -153,7 +153,63 @@ export function VendorsPage() {
|
||||
<Table.Td>{!isReadOnly && <ActionIcon variant="subtle" onClick={() => handleEdit(v)}><IconEdit size={16} /></ActionIcon>}</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
{filtered.length === 0 && <Table.Tr><Table.Td colSpan={8}><Text ta="center" c="dimmed" py="lg">No vendors yet</Text></Table.Td></Table.Tr>}
|
||||
{filtered.length === 0 && vendors.length === 0 && (
|
||||
<Table.Tr>
|
||||
<Table.Td colSpan={8} p={0}>
|
||||
<Card p="xl" style={{ textAlign: 'center' }}>
|
||||
<ThemeIcon size={60} radius="xl" variant="gradient" gradient={{ from: 'orange', to: 'yellow' }} mx="auto" mb="md">
|
||||
<IconUsers size={32} />
|
||||
</ThemeIcon>
|
||||
<Title order={3} mb="xs">Vendor Management</Title>
|
||||
<Text c="dimmed" maw={550} mx="auto" mb="lg">
|
||||
Keep track of your HOA's service providers, contractors, and suppliers.
|
||||
Having a centralized vendor directory helps with 1099 reporting, contract
|
||||
renewal tracking, and comparing year-over-year spending.
|
||||
</Text>
|
||||
<Card withBorder p="md" maw={550} mx="auto" mb="lg" ta="left">
|
||||
<Text fw={600} mb="xs">
|
||||
<IconBulb size={16} style={{ verticalAlign: 'middle', marginRight: 6 }} />
|
||||
Common HOA Vendors to Track
|
||||
</Text>
|
||||
<List size="sm" spacing="xs" c="dimmed">
|
||||
<List.Item><Text span fw={500} c="dark">Landscaping Company</Text> — Lawn care, tree trimming, seasonal planting</List.Item>
|
||||
<List.Item><Text span fw={500} c="dark">Property Management</Text> — Day-to-day management and tenant communications</List.Item>
|
||||
<List.Item><Text span fw={500} c="dark">Insurance Provider</Text> — Master policy for buildings and common areas</List.Item>
|
||||
<List.Item><Text span fw={500} c="dark">Pool Maintenance</Text> — Weekly chemical testing, cleaning, and equipment repair</List.Item>
|
||||
<List.Item><Text span fw={500} c="dark">Snow Removal / Paving</Text> — Winter plowing and parking lot maintenance</List.Item>
|
||||
<List.Item><Text span fw={500} c="dark">Attorney / CPA</Text> — Legal counsel and annual financial review</List.Item>
|
||||
</List>
|
||||
</Card>
|
||||
<Group justify="center" gap="md">
|
||||
{!isReadOnly && (
|
||||
<>
|
||||
<Button
|
||||
size="md"
|
||||
leftSection={<IconRocket size={18} />}
|
||||
variant="gradient"
|
||||
gradient={{ from: 'orange', to: 'yellow' }}
|
||||
onClick={() => { setEditing(null); form.reset(); open(); }}
|
||||
>
|
||||
Add Your First Vendor
|
||||
</Button>
|
||||
<Button
|
||||
size="md"
|
||||
variant="light"
|
||||
leftSection={<IconUpload size={16} />}
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
>
|
||||
Import from CSV
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</Group>
|
||||
</Card>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
)}
|
||||
{filtered.length === 0 && vendors.length > 0 && (
|
||||
<Table.Tr><Table.Td colSpan={8}><Text ta="center" c="dimmed" py="lg">No vendors match your search</Text></Table.Td></Table.Tr>
|
||||
)}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
<Modal opened={opened} onClose={close} title={editing ? 'Edit Vendor' : 'New Vendor'}>
|
||||
|
||||
Reference in New Issue
Block a user