clqms-fe1/COMPONENT_ORGANIZATION.md
mahdahar ae806911be feat(equipment,organization): add equipment API client and complete organization module structure
- Add equipment.js API client with full CRUD operations
- Add organization sub-routes: account, department, discipline, instrument, site, workstation
- Create EquipmentModal and DeleteConfirmModal components
- Update master-data navigation and sidebar
- Update tests, containers, counters, geography, locations, occupations, specialties, testmap, and valuesets pages
- Add COMPONENT_ORGANIZATION.md documentation
2026-02-24 16:53:04 +07:00

156 lines
4.4 KiB
Markdown

# Component Organization Guide
Guide for splitting large components and modals into manageable files.
## When to Split Components
Split a component when:
- File exceeds 200 lines
- Component has multiple distinct sections (tabs, steps, panels)
- Logic becomes hard to follow
- Multiple developers work on different parts
## Modal Organization Pattern
### Structure for Large Modals
```
src/routes/(app)/feature/
├── +page.svelte # Main page
├── FeatureModal.svelte # Main modal container
└── feature-modal/ # Modal sub-components (kebab-case folder)
├── modals/ # Nested modals
│ └── PickerModal.svelte
└── tabs/ # Tab content components
├── BasicInfoTab.svelte
├── SettingsTab.svelte
└── AdvancedTab.svelte
```
### Example: Test Form Modal
**Location**: `src/routes/(app)/master-data/tests/test-modal/`
```svelte
<!-- TestFormModal.svelte -->
<script>
import Modal from '$lib/components/Modal.svelte';
import BasicInfoTab from './test-modal/tabs/BasicInfoTab.svelte';
import TechDetailsTab from './test-modal/tabs/TechDetailsTab.svelte';
import CalcDetailsTab from './test-modal/tabs/CalcDetailsTab.svelte';
let { open = $bindable(false), test = null } = $props();
let activeTab = $state('basic');
let formData = $state({});
</script>
<Modal bind:open title={test ? 'Edit Test' : 'New Test'} size="xl">
{#snippet children()}
<div class="tabs tabs-boxed mb-4">
<button class="tab" class:tab-active={activeTab === 'basic'} onclick={() => activeTab = 'basic'}>Basic</button>
<button class="tab" class:tab-active={activeTab === 'technical'} onclick={() => activeTab = 'technical'}>Technical</button>
<button class="tab" class:tab-active={activeTab === 'calculation'} onclick={() => activeTab = 'calculation'}>Calculation</button>
</div>
{#if activeTab === 'basic'}
<BasicInfoTab bind:formData />
{:else if activeTab === 'technical'}
<TechDetailsTab bind:formData />
{:else if activeTab === 'calculation'}
<CalcDetailsTab bind:formData />
{/if}
{/snippet}
</Modal>
```
```svelte
<!-- test-modal/tabs/BasicInfoTab.svelte -->
<script>
let { formData = $bindable({}) } = $props();
</script>
<div class="space-y-4">
<div class="form-control">
<label class="label">Test Name</label>
<input class="input input-bordered" bind:value={formData.name} />
</div>
<div class="form-control">
<label class="label">Description</label>
<textarea class="textarea textarea-bordered" bind:value={formData.description}></textarea>
</div>
</div>
```
## Data Flow
### Parent to Child
- Pass data via props (`bind:formData`)
- Use `$bindable()` for two-way binding
- Keep state in parent when shared across tabs
### Child to Parent
- Use callbacks for actions (`onSave`, `onClose`)
- Modify bound data directly (with `$bindable`)
- Emit events for complex interactions
## Props Interface Pattern
```javascript
// Define props with JSDoc
/** @type {{ formData: Object, onValidate: Function, readonly: boolean }} */
let {
formData = $bindable({}),
onValidate = () => true,
readonly = false
} = $props();
```
## Naming Conventions
- **Main modal**: `{Feature}Modal.svelte` (e.g., `TestFormModal.svelte`)
- **Tab components**: `{TabName}Tab.svelte` (e.g., `BasicInfoTab.svelte`)
- **Nested modals**: `{Action}Modal.svelte` (e.g., `ConfirmDeleteModal.svelte`)
- **Folder names**: kebab-case matching the modal name (e.g., `test-modal/`)
## Shared State Management
For complex modals with shared state across tabs:
```javascript
// In main modal
let sharedState = $state({
dirty: false,
errors: {},
selectedItems: []
});
// Pass to tabs
<TabName formData={formData} sharedState={sharedState} />
```
## Import Order in Sub-components
Same as main components:
1. Svelte imports
2. `$lib/*` imports
3. External libraries
4. Relative imports (other tabs/modals)
## Testing Split Components
```bash
# Test individual tab component
vitest run src/routes/feature/modal-tabs/BasicInfoTab.test.js
# Test main modal integration
vitest run src/routes/feature/FeatureModal.test.js
```
## Benefits
- **Maintainability**: Each file has single responsibility
- **Collaboration**: Multiple developers can work on different tabs
- **Testing**: Test individual sections in isolation
- **Performance**: Only render visible tab content
- **Reusability**: Tabs can be used in different modals