Skip to content

Commit d171511

Browse files
Stefan ErnstStefan Ernst
authored andcommitted
Moved TimeCustomers back to its own handler, rename Customers -> Organisations
1 parent b08f3e3 commit d171511

10 files changed

Lines changed: 115 additions & 75 deletions

File tree

frontend/src/lib/api/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { time, timer } from './time.js';
99
import { statusCategories, statuses, workflows } from './workflows.js';
1010
import { assetSets, assetRoles, assetTypes, assetCategories, assetStatuses, assets, itemLinkedAssets } from './assets.js';
1111
import { portal, portalCustomers, contactRoles, customerOrganisations } from './portal.js';
12-
import { scmProviders, workspaceSCM, itemSCMLinks } from './scm.js';
12+
import { scmProviders, workspaceSCM, itemSCMLinks, userSCM } from './scm.js';
1313
import { channels, channelCategories, requestTypes } from './channels.js';
1414
import { milestoneCategories, milestones, iterationTypes, iterations } from './milestones.js';
1515
import { permissions, groups } from './permissions.js';
@@ -274,6 +274,9 @@ export const api = {
274274
// Item SCM Links
275275
itemSCMLinks,
276276

277+
// User SCM connections (personal OAuth tokens)
278+
userSCM,
279+
277280
// Security Settings (admin only)
278281
securitySettings,
279282

frontend/src/lib/dialogs/TimeCustomerModal.svelte

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
<script>
2-
import { createEventDispatcher, tick } from 'svelte';
2+
import { tick } from 'svelte';
33
import Modal from './Modal.svelte';
44
import Button from '../components/Button.svelte';
55
import Input from '../components/Input.svelte';
66
import Textarea from '../components/Textarea.svelte';
77
import CustomFieldRenderer from '../features/items/CustomFieldRenderer.svelte';
88
import Label from '../components/Label.svelte';
99
import { Camera, Trash2 } from 'lucide-svelte';
10-
11-
const dispatch = createEventDispatcher();
10+
import { api } from '../api.js';
1211
1312
// Props
1413
export let isOpen = false;
@@ -23,6 +22,8 @@
2322
export let customerOrgFields = [];
2423
export let attachmentsEnabled = false;
2524
export let isEditing = false;
25+
export let onsave = () => {};
26+
export let oncancel = () => {};
2627
2728
let uploadingAvatar = false;
2829
let showAvatarUpload = false;
@@ -34,7 +35,7 @@
3435
if (!files || files.length === 0) return;
3536
3637
if (!attachmentsEnabled) {
37-
alert('Attachments must be enabled to upload customer avatars');
38+
alert('Attachments must be enabled to upload organization avatars');
3839
return;
3940
}
4041
@@ -51,21 +52,11 @@
5152
uploadFormData.append('item_id', '0');
5253
uploadFormData.append('category', 'customer_avatar');
5354
54-
const response = await fetch('/api/attachments/upload', {
55-
method: 'POST',
56-
body: uploadFormData,
57-
});
58-
59-
if (!response.ok) {
60-
throw new Error(`Upload failed: ${response.statusText}`);
61-
}
62-
63-
const uploadResult = await response.json();
55+
const uploadResult = await api.attachments.upload(uploadFormData);
6456
6557
if (uploadResult && uploadResult.success && uploadResult.avatar_url) {
66-
formData.avatar_url = uploadResult.avatar_url;
58+
formData = { ...formData, avatar_url: uploadResult.avatar_url };
6759
showAvatarUpload = false;
68-
alert('Avatar uploaded successfully');
6960
}
7061
} catch (err) {
7162
alert('Failed to upload avatar: ' + (err.message || err));
@@ -75,7 +66,7 @@
7566
}
7667
7768
function removeAvatar() {
78-
formData.avatar_url = null;
69+
formData = { ...formData, avatar_url: null };
7970
}
8071
8172
function getInitials(name) {
@@ -89,12 +80,12 @@
8980
9081
function handleSubmit() {
9182
if (formData.name.trim()) {
92-
dispatch('save');
83+
onsave();
9384
}
9485
}
9586
9687
function handleCancel() {
97-
dispatch('cancel');
88+
oncancel();
9889
}
9990
10091
async function focusNameInput() {
@@ -125,12 +116,12 @@
125116
>
126117
<div class="p-6">
127118
<h3 class="text-xl font-semibold mb-6" style="color: var(--ds-text);">
128-
{isEditing ? 'Edit Customer' : 'New Customer'}
119+
{isEditing ? 'Edit Organization' : 'New Organization'}
129120
</h3>
130121

131122
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
132123
<div>
133-
<Label required class="mb-2">Customer Name</Label>
124+
<Label required class="mb-2">Organization Name</Label>
134125
<Input id={nameInputId} bind:inputRef={nameInputRef} bind:value={formData.name} required />
135126
</div>
136127

@@ -142,14 +133,14 @@
142133

143134
<!-- Avatar Upload Section -->
144135
<div class="mt-6">
145-
<Label class="mb-2">Customer Avatar</Label>
136+
<Label class="mb-2">Organization Avatar</Label>
146137

147138
<!-- Avatar Preview -->
148139
{#if formData.avatar_url}
149140
<div class="flex items-center gap-4 mb-3">
150141
<img
151142
src={formData.avatar_url}
152-
alt="Customer avatar"
143+
alt="Organization avatar"
153144
class="w-16 h-16 rounded object-cover"
154145
/>
155146
<div class="flex-1">
@@ -190,7 +181,7 @@
190181
</Button>
191182
{#if !attachmentsEnabled}
192183
<p class="text-xs mt-1" style="color: var(--ds-text-warning);">
193-
Attachments must be enabled to upload customer avatars
184+
Attachments must be enabled to upload organization avatars
194185
</p>
195186
{/if}
196187
</div>
@@ -227,7 +218,7 @@
227218
id="active"
228219
class="mr-3 w-4 h-4 text-blue-600 rounded focus:ring-2 focus:ring-blue-500"
229220
/>
230-
<label for="active" class="text-sm font-medium" style="color: var(--ds-text);">Active Customer</label>
221+
<label for="active" class="text-sm font-medium" style="color: var(--ds-text);">Active Organization</label>
231222
</div>
232223

233224
<!-- Custom Fields -->
@@ -257,7 +248,7 @@
257248
size="medium"
258249
keyboardHint={submitHint}
259250
>
260-
{isEditing ? 'Update' : 'Create'} Customer
251+
{isEditing ? 'Update' : 'Create'} Organization
261252
</Button>
262253
<Button
263254
variant="default"

frontend/src/lib/features/time/Time.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
1111
const tabs = [
1212
{ id: 'time-entry', label: 'Time Entry', icon: Clock, component: TimeEntry, route: '/time' },
13-
{ id: 'customers', label: 'Customers', icon: User, component: TimeCustomers, route: '/time/customers' },
13+
{ id: 'customers', label: 'Organizations', icon: User, component: TimeCustomers, route: '/time/customers' },
1414
{ id: 'projects', label: 'Projects', icon: Briefcase, component: TimeProjects, route: '/time/projects' },
1515
{ id: 'reports', label: 'Reports', icon: BarChart3, component: TimeReports, route: '/time/worklogs' }
1616
];
@@ -43,7 +43,7 @@
4343
<div class="p-6">
4444
<div class="mb-6">
4545
<h1 class="text-xl font-semibold" style="color: var(--ds-text);">Time & Projects</h1>
46-
<p class="mt-1 text-sm" style="color: var(--ds-text-subtle);">Manage customers, projects, and time tracking</p>
46+
<p class="mt-1 text-sm" style="color: var(--ds-text-subtle);">Manage organizations, projects, and time tracking</p>
4747
</div>
4848

4949
<!-- Navigation -->

frontend/src/lib/features/time/TimeCustomers.svelte

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
9999
async function saveCustomer() {
100100
try {
101+
console.log(formData)
101102
if (editingCustomer) {
102103
await api.time.customers.update(editingCustomer.id, formData);
103104
} else {
@@ -169,9 +170,9 @@
169170
<!-- Header -->
170171
<div class="mb-6 flex justify-between items-start">
171172
<div>
172-
<Text as="h2" size="lg" weight="semibold">Customers</Text>
173+
<Text as="h2" size="lg" weight="semibold">Organizations</Text>
173174
<Text as="div" size="xs" variant="subtle" class="mt-1">
174-
Manage your clients and customer information
175+
Manage your client organizations
175176
</Text>
176177
</div>
177178
<Button
@@ -181,7 +182,7 @@
181182
size="medium"
182183
keyboardHint={getShortcutDisplay('timeCustomers', 'addCustomer')}
183184
>
184-
Add Customer
185+
Add Organization
185186
</Button>
186187
</div>
187188
@@ -200,7 +201,7 @@
200201
columns={customerColumns}
201202
data={customers}
202203
keyField="id"
203-
emptyMessage="No customers found. Create your first customer to get started."
204+
emptyMessage="No organizations found. Create your first organization to get started."
204205
emptyIcon={Users}
205206
actionItems={buildCustomerDropdownItems}
206207
>

frontend/src/lib/workspaces/CustomerOrganisationNavigation.svelte

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script>
2-
import { Search, Building, Users, Plus } from 'lucide-svelte';
2+
import { Search, Users, Plus } from 'lucide-svelte';
33
import Button from '../components/Button.svelte';
4+
import Avatar from '../components/Avatar.svelte';
45
56
let {
67
organisations = [],
@@ -60,7 +61,9 @@
6061
onmouseenter={(e) => { if (selectedOrgId !== null && dragOverOrgId !== null) e.currentTarget.style.cssText = 'background: var(--ds-surface-hovered); color: var(--ds-text);'; }}
6162
onmouseleave={(e) => { if (selectedOrgId !== null && dragOverOrgId !== null) e.currentTarget.style.cssText = 'color: var(--ds-text-subtle);'; }}
6263
>
63-
<Users class="w-4 h-4 flex-shrink-0" />
64+
<div class="w-6 h-6 flex items-center justify-center flex-shrink-0">
65+
<Users class="w-4 h-4" />
66+
</div>
6467
<span class="flex-1 truncate">Unassigned</span>
6568
<span class="text-xs px-1.5 py-0.5 rounded" style="background: var(--ds-interactive-subtle);">{unassignedCount}</span>
6669
</button>
@@ -77,7 +80,13 @@
7780
onmouseenter={(e) => { if (!isActive && !isDragOver) e.currentTarget.style.cssText = 'background: var(--ds-surface-hovered); color: var(--ds-text);'; }}
7881
onmouseleave={(e) => { if (!isActive && !isDragOver) e.currentTarget.style.cssText = 'color: var(--ds-text-subtle);'; }}
7982
>
80-
<Building class="w-4 h-4 flex-shrink-0" />
83+
<Avatar
84+
src={org.avatar_url}
85+
name={org.name}
86+
size="xs"
87+
variant="blue"
88+
rounded="md"
89+
/>
8190
<span class="flex-1 truncate">{org.name}</span>
8291
<span class="text-xs px-1.5 py-0.5 rounded" style="background: var(--ds-interactive-subtle);">{customerCounts[org.id] || 0}</span>
8392
</button>

frontend/src/lib/workspaces/Customers.svelte

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
<script>
22
import { onMount } from 'svelte';
3+
import { navigate } from '../router.js';
34
import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
45
import { Users, Mail, Search, GripVertical, Plus, Edit2, Trash2, MoreHorizontal } from 'lucide-svelte';
56
import { api } from '../api.js';
67
import { confirm } from '../composables/useConfirm.js';
78
import { createShortcutHandler } from '../utils/keyboardShortcuts.js';
89
import Button from '../components/Button.svelte';
10+
import Avatar from '../components/Avatar.svelte';
911
import DropdownMenu from '../layout/DropdownMenu.svelte';
1012
import CustomFieldRenderer from '../features/items/CustomFieldRenderer.svelte';
1113
import { errorToast } from '../stores/toasts.svelte.js';
@@ -386,7 +388,7 @@
386388
{customerCounts}
387389
{dragOverOrgId}
388390
onSelect={selectOrganisation}
389-
onManageOrgs={() => {}}
391+
onManageOrgs={() => navigate('/time/customers')}
390392
/>
391393
392394
<!-- Main Content -->
@@ -402,17 +404,31 @@
402404
{:else}
403405
<!-- Header -->
404406
<div class="flex justify-between items-start mb-6">
405-
<div>
406-
<h1 class="text-xl font-semibold" style="color: var(--ds-text);">
407-
{#if selectedOrgId === null}
408-
Unassigned Customers
409-
{:else}
410-
{customerOrganisations.find(o => o.id === selectedOrgId)?.name || 'Customers'}
407+
<div class="flex items-center gap-4">
408+
{#if selectedOrgId !== null}
409+
{@const selectedOrg = customerOrganisations.find(o => o.id === selectedOrgId)}
410+
{#if selectedOrg}
411+
<Avatar
412+
src={selectedOrg.avatar_url}
413+
name={selectedOrg.name}
414+
size="lg"
415+
variant="blue"
416+
rounded="md"
417+
/>
411418
{/if}
412-
</h1>
413-
<p class="text-sm" style="color: var(--ds-text-subtle);">
414-
{filteredCustomers.length} customer{filteredCustomers.length !== 1 ? 's' : ''}
415-
</p>
419+
{/if}
420+
<div>
421+
<h1 class="text-xl font-semibold" style="color: var(--ds-text);">
422+
{#if selectedOrgId === null}
423+
Unassigned Customers
424+
{:else}
425+
{customerOrganisations.find(o => o.id === selectedOrgId)?.name || 'Customers'}
426+
{/if}
427+
</h1>
428+
<p class="text-sm" style="color: var(--ds-text-subtle);">
429+
{filteredCustomers.length} customer{filteredCustomers.length !== 1 ? 's' : ''}
430+
</p>
431+
</div>
416432
</div>
417433
<Button
418434
variant="primary"
@@ -525,7 +541,7 @@
525541
submitDisabled={!formData.name.trim() || !formData.email.trim()}
526542
onclose={closeModal}
527543
>
528-
{#snippet children(submitHint)}
544+
{#snippet children({ submitHint })}
529545
<ModalHeader title="Add Portal Customer" onClose={closeModal} />
530546
531547
<div class="p-6 space-y-4">
@@ -606,7 +622,7 @@
606622
submitDisabled={!editFormData.name.trim() || !editFormData.email.trim()}
607623
onclose={closeDetailModal}
608624
>
609-
{#snippet children(submitHint)}
625+
{#snippet children({ submitHint })}
610626
<ModalHeader title="Edit Portal Customer" icon={Edit2} onClose={closeDetailModal} />
611627
612628
<div class="p-6 space-y-4">

0 commit comments

Comments
 (0)