diff --git a/apps/contacts/apps/web/src/lib/components/NewContactModal.svelte b/apps/contacts/apps/web/src/lib/components/NewContactModal.svelte
index e02310dbf..73090fb9d 100644
--- a/apps/contacts/apps/web/src/lib/components/NewContactModal.svelte
+++ b/apps/contacts/apps/web/src/lib/components/NewContactModal.svelte
@@ -34,6 +34,22 @@
let country = $state('');
let notes = $state('');
+ // Social Media
+ let linkedin = $state('');
+ let twitter = $state('');
+ let facebook = $state('');
+ let instagram = $state('');
+ let xing = $state('');
+ let github = $state('');
+ let youtube = $state('');
+ let tiktok = $state('');
+ let telegram = $state('');
+ let whatsapp = $state('');
+ let signal = $state('');
+ let discord = $state('');
+ let bluesky = $state('');
+ let socialSectionOpen = $state(false);
+
const initials = $derived(() => {
const f = firstName?.[0] || '';
const l = lastName?.[0] || '';
@@ -120,6 +136,20 @@
postalCode: postalCode || null,
country: country || null,
notes: notes || null,
+ // Social Media
+ linkedin: linkedin || null,
+ twitter: twitter || null,
+ facebook: facebook || null,
+ instagram: instagram || null,
+ xing: xing || null,
+ github: github || null,
+ youtube: youtube || null,
+ tiktok: tiktok || null,
+ telegram: telegram || null,
+ whatsapp: whatsapp || null,
+ signal: signal || null,
+ discord: discord || null,
+ bluesky: bluesky || null,
});
// Upload photo if selected
@@ -503,6 +533,214 @@
>
+
+
+
@@ -957,5 +1195,66 @@
.actions {
flex-direction: column-reverse;
}
+
+ .social-grid {
+ grid-template-columns: 1fr;
+ }
+ }
+
+ /* Social Media Section */
+ .section-header-toggle {
+ width: 100%;
+ background: none;
+ border: none;
+ cursor: pointer;
+ border-bottom: 1px solid hsl(var(--color-border) / 0.5);
+ margin-bottom: 0;
+ }
+
+ .section-header-toggle:hover {
+ background: hsl(var(--color-surface-hover) / 0.3);
+ margin: 0 -1rem;
+ padding: 0 1rem 0.625rem;
+ width: calc(100% + 2rem);
+ border-radius: 0.5rem 0.5rem 0 0;
+ }
+
+ .chevron-icon {
+ width: 1rem;
+ height: 1rem;
+ margin-left: auto;
+ color: hsl(var(--color-muted-foreground));
+ transition: transform 0.2s ease;
+ }
+
+ .chevron-open {
+ transform: rotate(180deg);
+ }
+
+ .social-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 0.75rem;
+ padding-top: 0.75rem;
+ }
+
+ .social-label {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ }
+
+ .social-icon-label {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 1.25rem;
+ height: 1.25rem;
+ border-radius: 0.25rem;
+ background: hsl(var(--color-primary) / 0.1);
+ color: hsl(var(--color-primary));
+ font-size: 0.625rem;
+ font-weight: 700;
+ text-transform: lowercase;
}
diff --git a/apps/contacts/apps/web/src/routes/(app)/network/+page.svelte b/apps/contacts/apps/web/src/routes/(app)/network/+page.svelte
index b84c582a4..7441545fa 100644
--- a/apps/contacts/apps/web/src/routes/(app)/network/+page.svelte
+++ b/apps/contacts/apps/web/src/routes/(app)/network/+page.svelte
@@ -13,6 +13,21 @@
networkStore.setSearch(contactsFilterStore.searchQuery);
});
+ // Refocus view when search results change
+ let previousNodeCount = $state(0);
+ $effect(() => {
+ const currentNodeCount = networkStore.nodes.length;
+ const hasSearch = contactsFilterStore.searchQuery.length > 0;
+
+ // If search is active and node count changed, reset zoom to show all results
+ if (hasSearch && currentNodeCount !== previousNodeCount && currentNodeCount > 0) {
+ setTimeout(() => {
+ graphComponent?.resetZoom();
+ }, 100);
+ }
+ previousNodeCount = currentNodeCount;
+ });
+
let graphComponent: NetworkGraph;
let graphContainer: HTMLDivElement;
@@ -179,10 +194,10 @@
flex-direction: column;
}
- /* Floating Controls */
+ /* Floating Controls - positioned above QuickInputBar and PillNav */
.controls-wrapper {
- position: absolute;
- top: 1rem;
+ position: fixed;
+ bottom: calc(140px + env(safe-area-inset-bottom));
left: 50%;
transform: translateX(-50%);
z-index: 10;
@@ -192,7 +207,7 @@
/* Error Banner */
.error-banner {
position: absolute;
- top: 5rem;
+ top: 1rem;
left: 50%;
transform: translateX(-50%);
z-index: 10;
@@ -219,9 +234,11 @@
/* Modal Sidebar Wrapper - Override modal positioning */
.modal-sidebar-wrapper {
position: fixed;
- top: 5rem; /* Below the pill nav */
+ top: 1rem;
right: 1rem;
- bottom: 1rem;
+ bottom: calc(
+ 200px + env(safe-area-inset-bottom)
+ ); /* Above controls + QuickInputBar + PillNav */
width: 400px;
max-width: calc(100vw - 2rem);
z-index: 50;
@@ -291,7 +308,7 @@
@media (max-width: 768px) {
.controls-wrapper {
- top: 1rem;
+ bottom: calc(160px + env(safe-area-inset-bottom));
width: calc(100% - 2rem);
max-width: none;
}