Skip to content

Commit ec471ae

Browse files
authored
Merge pull request #51 from MicroPyramid/dev
Dev
2 parents 5b1075e + 34cc6ec commit ec471ae

File tree

13 files changed

+902
-75
lines changed

13 files changed

+902
-75
lines changed

.github/copilot-instructions.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ BottleCRM is a modern CRM application built with:
1717
- **Framework**: SvelteKit 2.21.x, Svelte 5.x, Prisma
1818
- **Styling**: tailwind 4.1.x css
1919
- **Database**: postgresql
20-
- **Icons**: fontawesome, we are migrating to lucide icons
20+
- **Icons**: lucide icons
2121

2222
## Important Notes
23-
- We need to ensure access control is strictly enforced based on user roles. No record is accessible unless the user or the org has the appropriate permissions.
23+
- We need to ensure access control is strictly enforced based on user roles.
24+
- No record should be accessible unless the user or the org has the appropriate permissions.
2425
- When implementing forms in sveltekit A form label must be associated with a control

src/lib/data/accountOwnership.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const accountOwnership = [
2+
['', 'Select Ownership'],
3+
['PUBLIC', 'Public'],
4+
['PRIVATE', 'Private'],
5+
['SUBSIDIARY', 'Subsidiary'],
6+
['NON_PROFIT', 'Non-Profit'],
7+
['GOVERNMENT', 'Government'],
8+
['OTHER', 'Other']
9+
];

src/lib/data/accountTypes.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const accountTypes = [
2+
['', 'Select Type'],
3+
['CUSTOMER', 'Customer'],
4+
['PARTNER', 'Partner'],
5+
['PROSPECT', 'Prospect'],
6+
['VENDOR', 'Vendor'],
7+
['COMPETITOR', 'Competitor'],
8+
['OTHER', 'Other']
9+
];

src/lib/data/countries.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
export const countries = [
2+
['', 'Select Country'],
3+
['US', 'United States'],
4+
['UK', 'United Kingdom'],
5+
['CA', 'Canada'],
6+
['AU', 'Australia'],
7+
['IN', 'India'],
8+
['DE', 'Germany'],
9+
['FR', 'France'],
10+
['JP', 'Japan'],
11+
['CN', 'China'],
12+
['BR', 'Brazil'],
13+
['MX', 'Mexico'],
14+
['IT', 'Italy'],
15+
['ES', 'Spain'],
16+
['NL', 'Netherlands'],
17+
['SE', 'Sweden'],
18+
['NO', 'Norway'],
19+
['DK', 'Denmark'],
20+
['FI', 'Finland'],
21+
['CH', 'Switzerland'],
22+
['AT', 'Austria'],
23+
['BE', 'Belgium'],
24+
['IE', 'Ireland'],
25+
['PL', 'Poland'],
26+
['RU', 'Russia'],
27+
['KR', 'South Korea'],
28+
['SG', 'Singapore'],
29+
['TH', 'Thailand'],
30+
['MY', 'Malaysia'],
31+
['ID', 'Indonesia'],
32+
['PH', 'Philippines'],
33+
['VN', 'Vietnam'],
34+
['NZ', 'New Zealand'],
35+
['ZA', 'South Africa'],
36+
['EG', 'Egypt'],
37+
['NG', 'Nigeria'],
38+
['KE', 'Kenya'],
39+
['AR', 'Argentina'],
40+
['CL', 'Chile'],
41+
['CO', 'Colombia'],
42+
['PE', 'Peru'],
43+
['OTHER', 'Other']
44+
];

src/lib/data/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Re-export all data constants for easy importing
2+
export { industries } from './industries.js';
3+
export { accountTypes } from './accountTypes.js';
4+
export { accountOwnership } from './accountOwnership.js';
5+
export { ratings } from './ratings.js';
6+
export { countries } from './countries.js';
7+
export { leadSources } from './leadSources.js';
8+
export { leadStatuses } from './leadStatuses.js';

src/lib/data/industries.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export const industries = [
2+
['', 'Select Industry'],
3+
['TECHNOLOGY', 'Technology'],
4+
['HEALTHCARE', 'Healthcare'],
5+
['FINANCE', 'Finance'],
6+
['EDUCATION', 'Education'],
7+
['RETAIL', 'Retail'],
8+
['MANUFACTURING', 'Manufacturing'],
9+
['ENERGY', 'Energy'],
10+
['REAL_ESTATE', 'Real Estate'],
11+
['CONSTRUCTION', 'Construction'],
12+
['TRANSPORTATION', 'Transportation'],
13+
['HOSPITALITY', 'Hospitality'],
14+
['AGRICULTURE', 'Agriculture'],
15+
['OTHER', 'Other']
16+
];

src/lib/data/leadSources.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export const leadSources = [
2+
['', 'Select Source'],
3+
['WEB', 'Website'],
4+
['PHONE_INQUIRY', 'Phone Inquiry'],
5+
['PARTNER_REFERRAL', 'Partner Referral'],
6+
['COLD_CALL', 'Cold Call'],
7+
['TRADE_SHOW', 'Trade Show'],
8+
['EMPLOYEE_REFERRAL', 'Employee Referral'],
9+
['ADVERTISEMENT', 'Advertisement'],
10+
['SOCIAL_MEDIA', 'Social Media'],
11+
['EMAIL_CAMPAIGN', 'Email Campaign'],
12+
['WEBINAR', 'Webinar'],
13+
['CONTENT_MARKETING', 'Content Marketing'],
14+
['SEO', 'SEO/Organic Search'],
15+
['PPC', 'Pay-Per-Click Advertising'],
16+
['DIRECT_MAIL', 'Direct Mail'],
17+
['OTHER', 'Other']
18+
];

src/lib/data/leadStatuses.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const leadStatuses = [
2+
['NEW', 'New'],
3+
['PENDING', 'Pending'],
4+
['CONTACTED', 'Contacted'],
5+
['QUALIFIED', 'Qualified'],
6+
['UNQUALIFIED', 'Unqualified']
7+
];

src/lib/data/ratings.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const ratings = [
2+
['', 'Select Rating'],
3+
['HOT', '🔥 Hot'],
4+
['WARM', '🟡 Warm'],
5+
['COLD', '🟦 Cold']
6+
];

src/routes/(app)/Sidebar.svelte

Lines changed: 69 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
3333
let isDark = $state(false);
3434
let userDropdownOpen = $state(false);
35+
let dropdownRef = $state();
3536
3637
const closeDrawer = () => {
3738
drawerHidden = true;
@@ -46,6 +47,38 @@
4647
userDropdownOpen = !userDropdownOpen;
4748
};
4849
50+
const handleSettingsLinkClick = (event, href) => {
51+
event.preventDefault();
52+
event.stopPropagation();
53+
54+
// Close the dropdown
55+
userDropdownOpen = false;
56+
57+
// Navigate to the intended URL
58+
window.location.href = href;
59+
};
60+
61+
const handleDropdownClick = (event) => {
62+
// Prevent clicks inside dropdown from bubbling up
63+
event.stopPropagation();
64+
};
65+
66+
const handleClickOutside = (event) => {
67+
if (userDropdownOpen && dropdownRef && !dropdownRef.contains(event.target)) {
68+
userDropdownOpen = false;
69+
}
70+
};
71+
72+
// Add click outside listener
73+
$effect(() => {
74+
if (typeof document !== 'undefined') {
75+
document.addEventListener('click', handleClickOutside);
76+
return () => {
77+
document.removeEventListener('click', handleClickOutside);
78+
};
79+
}
80+
});
81+
4982
let mainSidebarUrl = $derived($page.url.pathname);
5083
let openDropdowns = $state({});
5184
@@ -86,10 +119,14 @@
86119
]
87120
},
88121
{
89-
href: '/app/accounts',
122+
key: 'accounts',
90123
label: 'Accounts',
91124
icon: Building,
92-
type: 'link'
125+
type: 'dropdown',
126+
children: [
127+
{ href: '/app/accounts', label: 'All Accounts', icon: List },
128+
{ href: '/app/accounts/new', label: 'New Account', icon: Plus }
129+
]
93130
},
94131
{
95132
key: 'opportunities',
@@ -205,8 +242,8 @@
205242
</nav>
206243
</div>
207244
208-
<!-- User profile section - moved to bottom -->
209-
<div class="p-4 border-t border-gray-200 dark:border-gray-700">
245+
<!-- settings section -->
246+
<div class="p-4 border-t border-gray-200 dark:border-gray-700" bind:this={dropdownRef}>
210247
<div class="flex items-center gap-3 mb-3">
211248
<img class="w-10 h-10 rounded-lg object-cover" src={user.profilePhoto} alt="User avatar" />
212249
<div class="flex-1 min-w-0">
@@ -238,38 +275,48 @@
238275
</button>
239276
</div>
240277
241-
<!-- User dropdown menu -->
278+
<!-- settings dropdown menu -->
242279
{#if userDropdownOpen}
243-
<div class="mt-3 p-1 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
244-
<a
245-
href="/app/profile"
246-
class="flex items-center gap-3 px-3 py-2 text-sm text-gray-700 hover:bg-white dark:text-gray-300 dark:hover:bg-gray-700 rounded transition-colors"
280+
<div
281+
class="mt-3 p-1 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700"
282+
onclick={handleDropdownClick}
283+
onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') handleDropdownClick(e); }}
284+
tabindex="0"
285+
role="menu"
286+
>
287+
<button
288+
type="button"
289+
onclick={(e) => handleSettingsLinkClick(e, '/app/profile')}
290+
class="flex items-center gap-3 px-3 py-2 text-sm text-gray-700 hover:bg-white dark:text-gray-300 dark:hover:bg-gray-700 rounded transition-colors w-full text-left"
247291
>
248292
<User class="w-4 h-4" />
249293
Profile
250-
</a>
251-
<a
252-
href="/app/users"
253-
class="flex items-center gap-3 px-3 py-2 text-sm text-gray-700 hover:bg-white dark:text-gray-300 dark:hover:bg-gray-700 rounded transition-colors"
294+
</button>
295+
<button
296+
type="button"
297+
onclick={(e) => handleSettingsLinkClick(e, '/app/users')}
298+
class="flex items-center gap-3 px-3 py-2 text-sm text-gray-700 hover:bg-white dark:text-gray-300 dark:hover:bg-gray-700 rounded transition-colors w-full text-left"
254299
>
255300
<Users class="w-4 h-4" />
256301
Users
257-
</a>
258-
<a
259-
href="/org"
260-
class="flex items-center gap-3 px-3 py-2 text-sm text-gray-700 hover:bg-white dark:text-gray-300 dark:hover:bg-gray-700 rounded transition-colors"
302+
</button>
303+
<button
304+
type="button"
305+
onclick={(e) => handleSettingsLinkClick(e, '/org')}
306+
class="flex items-center gap-3 px-3 py-2 text-sm text-gray-700 hover:bg-white dark:text-gray-300 dark:hover:bg-gray-700 rounded transition-colors w-full text-left"
261307
>
262308
<Building class="w-4 h-4" />
263309
Organizations
264-
</a>
310+
</button>
265311
<hr class="my-1 border-gray-200 dark:border-gray-600" />
266-
<a
267-
href="/logout"
268-
class="flex items-center gap-3 px-3 py-2 text-sm text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20 rounded transition-colors"
312+
<button
313+
type="button"
314+
onclick={(e) => handleSettingsLinkClick(e, '/logout')}
315+
class="flex items-center gap-3 px-3 py-2 text-sm text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20 rounded transition-colors w-full text-left"
269316
>
270317
<LogOut class="w-4 h-4" />
271318
Sign out
272-
</a>
319+
</button>
273320
</div>
274321
{/if}
275322
</div>

0 commit comments

Comments
 (0)