diff --git a/frontend/bun.lockb b/frontend/bun.lockb index e34de90..63e774e 100755 Binary files a/frontend/bun.lockb and b/frontend/bun.lockb differ diff --git a/frontend/package.json b/frontend/package.json index 42de99c..1060b4c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,7 @@ "clsx": "^2.1.1", "fuse.js": "^7.1.0", "solid-js": "^1.9.3", + "solid-motionone": "^1.0.3", "tailwind-scrollbar-hide": "^2.0.0", "valibot": "^1.0.0-rc.2" }, diff --git a/frontend/src-tauri/src/lib.rs b/frontend/src-tauri/src/lib.rs index b30297d..63395df 100644 --- a/frontend/src-tauri/src/lib.rs +++ b/frontend/src-tauri/src/lib.rs @@ -116,6 +116,7 @@ pub fn run() { .setup(|app| { let win_builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default()) .inner_size(480.0, 360.0) + .hidden_title(true) .resizable(true); // set transparent title bar only when building for macOS #[cfg(target_os = "macos")] diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6d901c4..f273923 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,129 +1,123 @@ -import { Search } from "@kobalte/core/search"; -import { useNavigate } from "@solidjs/router"; -import { IconRefresh, IconSearch } from "@tabler/icons-solidjs"; +import { IconSearch } from "@tabler/icons-solidjs"; import clsx from "clsx"; import Fuse from "fuse.js"; -import { createEffect, createResource, createSignal } from "solid-js"; -import { getUserImages } from "./network"; +import { For, createSignal } from "solid-js"; +import { SearchCardContact } from "./components/search-card/SearchCardContact"; +import { SearchCardEvent } from "./components/search-card/SearchCardEvent"; +import { SearchCardLocation } from "./components/search-card/SearchCardLocation"; +import { SearchCardNote } from "./components/search-card/SearchCardNote"; +import { SearchCardReceipt } from "./components/search-card/SearchCardReceipt"; +import { SearchCardWebsite } from "./components/search-card/SearchCardWebsite"; +import { sampleData } from "./network/sampleData"; +import type { DataItem } from "./network/types"; +import { getCardSize } from "./utils/getCardSize"; -type UserImages = Awaited>; +const getCardComponent = (item: DataItem) => { + switch (item.type) { + case "Location": + return ; + case "Event": + return ; + case "Contact": + return ; + case "Website": + return ; + case "Note": + return ; + case "Receipt": + return ; + default: + return null; + } +}; function App() { - const [searchResults, setSearchResults] = createSignal< - UserImages[number]["Text"] - >([]); + const [searchResults, setSearchResults] = createSignal([]); + const [searchQuery, setSearchQuery] = createSignal(""); + const [selectedItem, setSelectedItem] = createSignal(null); - const [images] = createResource(getUserImages); - - const nav = useNavigate(); - - let fuze = new Fuse[number]>([], { - keys: ["ImageText"], + const fuze = new Fuse(sampleData, { + keys: [{ name: "title", weight: 2 }, "rawData"], + threshold: 0.4, }); - // TODO: there's probably a better way? - createEffect(() => { - const userImages = images(); - - if (userImages == null) { - return; - } - - const imageText = userImages.flatMap((i) => i.Text ?? []); - - fuze = new Fuse(imageText, { - keys: ["ImageText"], - threshold: 0.3, - }); - }); - - const onInputChange = (query: string) => { - // TODO: we can migrate this searching to Rust, so we don't abuse the main thread. - // But, it's not too bad as is. - setSearchResults(fuze.search(query).flatMap((s) => s.item)); + const onInputChange = (event: InputEvent) => { + const query = (event.target as HTMLInputElement).value; + setSearchQuery(query); + setSearchResults(fuze.search(query).map((s) => s.item)); }; return ( -
- { - if (item?.ImageID == null) { - console.error("ImageID was null"); - return; - } - - nav(`/image/${item.ImageID}`); - }} - optionValue="ID" - optionLabel="ImageText" - placeholder="Search for stuff..." - itemComponent={(props) => ( - - - {props.item.rawValue.ImageText ?? ""} - - - )} - > + <> +
- - - - - } - > - - - - - - -
- -
- - {/* - No results found - */} -
- - {/*
-
-
-
-
-
-
-
- - - {(image) => ( - - +
+ - - )} - -
*/} -
- footer -
-
+ + + + + +
+
+ {searchResults().length > 0 ? ( +
+ + {(item) => ( +
+ setSelectedItem(item) + } + onKeyDown={(e) => { + if (e.key === "Enter") { + setSelectedItem(item); + } + }} + class={clsx( + "h-[144px] border relative border-neutral-200 cursor-pointer overflow-hidden rounded-xl", + { + "col-span-3": + getCardSize( + item.type, + ) === "1/1", + "col-span-6": + getCardSize( + item.type, + ) === "2/1", + }, + )} + > + + {item.title} + + {getCardComponent(item)} +
+ )} +
+
+ ) : searchQuery() !== "" ? ( +
+ No results found +
+ ) : null} +
+
+ +
+ footer +
+
+ ); } diff --git a/frontend/src/assets/fonts/Manrope-VariableFont_wght.ttf b/frontend/src/assets/fonts/Manrope-VariableFont_wght.ttf deleted file mode 100644 index f39ca39..0000000 Binary files a/frontend/src/assets/fonts/Manrope-VariableFont_wght.ttf and /dev/null differ diff --git a/frontend/src/assets/fonts/Switzer-Variable.ttf b/frontend/src/assets/fonts/Switzer-Variable.ttf new file mode 100644 index 0000000..d6e5cc4 Binary files /dev/null and b/frontend/src/assets/fonts/Switzer-Variable.ttf differ diff --git a/frontend/src/components/search-card/SearchCardContact.tsx b/frontend/src/components/search-card/SearchCardContact.tsx new file mode 100644 index 0000000..e817e34 --- /dev/null +++ b/frontend/src/components/search-card/SearchCardContact.tsx @@ -0,0 +1,26 @@ +import { Separator } from "@kobalte/core/separator"; + +import { IconUser } from "@tabler/icons-solidjs"; +import type { Contact } from "../../network/types"; + +type Props = { + item: Contact; +}; + +export const SearchCardContact = ({ item }: Props) => { + const { data } = item; + + return ( +
+
+

{data.name}

+ +
+

{data.phoneNumber}

+ +

+ {data.notes} +

+
+ ); +}; diff --git a/frontend/src/components/search-card/SearchCardEvent.tsx b/frontend/src/components/search-card/SearchCardEvent.tsx new file mode 100644 index 0000000..8febd33 --- /dev/null +++ b/frontend/src/components/search-card/SearchCardEvent.tsx @@ -0,0 +1,33 @@ +import { Separator } from "@kobalte/core/separator"; + +import { IconCalendar } from "@tabler/icons-solidjs"; +import type { Event } from "../../network/types"; + +type Props = { + item: Event; +}; + +export const SearchCardEvent = ({ item }: Props) => { + const { data } = item; + + return ( +
+
+

{data.title}

+ +
+

+ Organized by {data.organizer.name} on{" "} + {new Date(data.dateTime.start).toLocaleDateString("en-US", { + month: "long", + day: "numeric", + year: "numeric", + })} +

+ +

+ {data.description} +

+
+ ); +}; diff --git a/frontend/src/components/search-card/SearchCardLocation.tsx b/frontend/src/components/search-card/SearchCardLocation.tsx new file mode 100644 index 0000000..56f47ac --- /dev/null +++ b/frontend/src/components/search-card/SearchCardLocation.tsx @@ -0,0 +1,26 @@ +import { Separator } from "@kobalte/core/separator"; + +import { IconMapPin } from "@tabler/icons-solidjs"; +import type { Location } from "../../network/types"; + +type Props = { + item: Location; +}; + +export const SearchCardLocation = ({ item }: Props) => { + const { data } = item; + + return ( +
+
+

{data.name}

+ +
+

{data.address}

+ +

+ {data.description} +

+
+ ); +}; diff --git a/frontend/src/components/search-card/SearchCardNote.tsx b/frontend/src/components/search-card/SearchCardNote.tsx new file mode 100644 index 0000000..746dfcc --- /dev/null +++ b/frontend/src/components/search-card/SearchCardNote.tsx @@ -0,0 +1,26 @@ +import { Separator } from "@kobalte/core/separator"; + +import { IconNote } from "@tabler/icons-solidjs"; +import type { Note } from "../../network/types"; + +type Props = { + item: Note; +}; + +export const SearchCardNote = ({ item }: Props) => { + const { data } = item; + + return ( +
+
+

{data.title}

+ +
+

{data.keywords}

+ +

+ {data.content} +

+
+ ); +}; diff --git a/frontend/src/components/search-card/SearchCardReceipt.tsx b/frontend/src/components/search-card/SearchCardReceipt.tsx new file mode 100644 index 0000000..6d7dfad --- /dev/null +++ b/frontend/src/components/search-card/SearchCardReceipt.tsx @@ -0,0 +1,30 @@ +import { Separator } from "@kobalte/core/separator"; + +import { IconReceipt } from "@tabler/icons-solidjs"; +import type { Receipt } from "../../network/types"; + +type Props = { + item: Receipt; +}; + +export const SearchCardReceipt = ({ item }: Props) => { + const { data } = item; + + return ( +
+
+

+ {data.orderNumber} - {data.vendor} +

+ +
+

+ {data.shippingAddress.address} +

+ +

+ {data.amount} {data.currency} +

+
+ ); +}; diff --git a/frontend/src/components/search-card/SearchCardWebsite.tsx b/frontend/src/components/search-card/SearchCardWebsite.tsx new file mode 100644 index 0000000..0099128 --- /dev/null +++ b/frontend/src/components/search-card/SearchCardWebsite.tsx @@ -0,0 +1,26 @@ +import { Separator } from "@kobalte/core/separator"; + +import { IconLink } from "@tabler/icons-solidjs"; +import type { Website } from "../../network/types"; + +type Props = { + item: Website; +}; + +export const SearchCardWebsite = ({ item }: Props) => { + const { data } = item; + + return ( +
+
+

{data.title}

+ +
+

{data.url}

+ +

+ {data.description} +

+
+ ); +}; diff --git a/frontend/src/index.css b/frontend/src/index.css index 3deaf14..4221ff1 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -2,16 +2,68 @@ @tailwind components; @tailwind utilities; +/* CSS Reset */ +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html, +body { + height: 100%; +} + +body { + line-height: 1.2; + -webkit-font-smoothing: antialiased; +} + +img, +picture, +video, +canvas, +svg { + display: block; + max-width: 100%; +} + +input, +button, +textarea, +select { + font: inherit; +} + +p, +h1, +h2, +h3, +h4, +h5, +h6 { + overflow-wrap: break-word; +} + +#root, +#__next { + isolation: isolate; +} + @font-face { - font-family: "Manrope"; - src: url("./assets/fonts/Manrope-VariableFont_wght.ttf") format("truetype"); + font-family: "Switzer"; + src: url("./assets/fonts/Switzer-Variable.ttf") format("truetype"); font-weight: 100 900; font-display: swap; } :root { @apply bg-neutral-100 text-black rounded-xl; - font-family: Manrope, sans-serif; + font-family: "Switzer", sans-serif; + font-stretch: 100%; + font-optical-sizing: auto; font-size: 16px; line-height: 24px; font-weight: 500; diff --git a/frontend/src/network/sample-data.ts b/frontend/src/network/sample-data.ts deleted file mode 100644 index 342ee51..0000000 --- a/frontend/src/network/sample-data.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { DataArray } from "./types"; - -export const sampleData: DataArray = [ - { - type: "Event", - data: { - title: "Startup Mixer", - dateTime: { - start: "2025-06-01T19:00:00+01:00", - end: "2025-06-01T23:00:00+01:00", - }, - location: "The Kings Arms, 27 Ropemaker St, London EC2Y 9LY", - description: - "Casual networking event for tech entrepreneurs and investors in London.", - organizer: { - name: "London Startup Network", - email: "events@londonstartupnetwork.co.uk", - }, - attendees: ["Alex Smith", "Sofia Rodriguez"], - category: "Networking", - }, - }, - { - type: "Contact", - data: { - name: "João Silva", - phoneNumber: "+351 912 345 678", - emailAddress: "joao.silva@example.pt", - address: "Rua do Carmo 12, 1200-161 Lisboa, Portugal", - organization: "PortoTech Solutions", - title: "Marketing Manager", - notes: "Met at Web Summit Lisbon 2024", - }, - }, - { - type: "Location", - data: { - name: "Barman Dictat", - address: "14 Khreshchatyk St., Kyiv, Ukraine", - category: "Bar", - description: - "Stylish cocktail bar in the heart of Kyiv with an extensive menu of craft cocktails and a sophisticated atmosphere", - }, - }, - { - type: "Note", - data: { - title: "Q2 2025 Marketing Strategy", - keywords: ["strategy", "digital marketing", "Q2", "2025"], - content: - "## Executive Summary\n\nOur Q2 2025 marketing strategy focuses on expanding our digital presence and increasing customer engagement across all platforms. We will leverage AI-driven personalization to enhance user experience and implement a multi-channel approach to reach our target demographics more effectively.\n\n### Key Objectives\n\n1. Increase website traffic by 30% through SEO optimization and content marketing.\n2. Boost social media engagement rates by 25% using interactive campaigns and influencer partnerships.\n3. Implement a new customer loyalty program to improve retention rates by 15%.\n\nBy aligning our marketing efforts with emerging trends and customer preferences, we aim to solidify our market position and drive sustainable growth throughout Q2 and beyond.", - }, - }, - { - type: "Website", - data: { - url: "https://www.attio.com", - title: "Attio", - description: - "Attio is the AI-native CRM that builds, scales and grows your company to the next level.", - category: "SaaS", - }, - }, - { - type: "Receipt", - data: { - receiptDate: "2025-03-12T20:15:30+01:00", - orderNumber: "ORD12345", - amount: 49.99, - currency: "GBP", - vendor: "Zara Online Store", - items: [ - { - name: "Slim Fit Dress Shirt", - quantity: 1, - price: 49.99, - currency: "GBP", - }, - ], - paymentMethod: "Visa", - shippingAddress: { - name: "Alex Smith", - address: "123 High St, London, EC2A 3AZ", - }, - category: "Online Shopping", - }, - }, -]; diff --git a/frontend/src/network/sampleData.ts b/frontend/src/network/sampleData.ts new file mode 100644 index 0000000..8c1f992 --- /dev/null +++ b/frontend/src/network/sampleData.ts @@ -0,0 +1,436 @@ +import type { DataArray, DataItem } from "./types"; + +const getRawData = (item: DataItem) => { + return Object.values(item.data).join(" "); +}; + +export const sampleData: DataArray = [ + { + id: "1", + type: "Event", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.title; + }, + data: { + title: "Startup Mixer", + dateTime: { + start: "2025-06-01T19:00:00+01:00", + end: "2025-06-01T23:00:00+01:00", + }, + location: "The Kings Arms, 27 Ropemaker St, London EC2Y 9LY", + description: + "Casual networking event for tech entrepreneurs and investors in London.", + organizer: { + name: "London Startup Network", + email: "events@londonstartupnetwork.co.uk", + }, + attendees: ["Alex Smith", "Sofia Rodriguez"], + category: "Networking", + }, + }, + { + id: "2", + type: "Contact", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.name; + }, + data: { + name: "João Silva", + phoneNumber: "+351 912 345 678", + emailAddress: "joao.silva@example.pt", + address: "Rua do Carmo 12, 1200-161 Lisboa, Portugal", + organization: "PortoTech Solutions", + title: "Marketing Manager", + notes: "Met at Web Summit Lisbon 2024", + }, + }, + { + id: "3", + type: "Location", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.name; + }, + data: { + name: "Barman Dictat", + address: "14 Khreshchatyk St., Kyiv, Ukraine", + category: "Bar", + description: + "Stylish cocktail bar in the heart of Kyiv with an extensive menu of craft cocktails and a sophisticated atmosphere", + }, + }, + { + id: "4", + type: "Note", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.title; + }, + data: { + title: "Q2 2025 Marketing Strategy", + keywords: ["strategy", "digital marketing", "Q2", "2025"], + content: + "## Executive Summary\n\nOur Q2 2025 marketing strategy focuses on expanding our digital presence and increasing customer engagement across all platforms. We will leverage AI-driven personalization to enhance user experience and implement a multi-channel approach to reach our target demographics more effectively.\n\n### Key Objectives\n\n1. Increase website traffic by 30% through SEO optimization and content marketing.\n2. Boost social media engagement rates by 25% using interactive campaigns and influencer partnerships.\n3. Implement a new customer loyalty program to improve retention rates by 15%.\n\nBy aligning our marketing efforts with emerging trends and customer preferences, we aim to solidify our market position and drive sustainable growth throughout Q2 and beyond.", + }, + }, + { + id: "5", + type: "Website", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.title; + }, + data: { + url: "https://www.attio.com", + title: "Attio", + description: + "Attio is the AI-native CRM that builds, scales and grows your company to the next level.", + category: "SaaS", + }, + }, + { + id: "6", + type: "Receipt", + get rawData() { + return getRawData(this); + }, + get title() { + return `${this.data.orderNumber} - ${this.data.vendor}`; + }, + data: { + receiptDate: "2025-03-12T20:15:30+01:00", + orderNumber: "ORD12345", + amount: 49.99, + currency: "GBP", + vendor: "Zara Online Store", + items: [ + { + name: "Slim Fit Dress Shirt", + quantity: 1, + price: 49.99, + currency: "GBP", + }, + ], + paymentMethod: "Visa", + shippingAddress: { + name: "Alex Smith", + address: "123 High St, London, EC2A 3AZ", + }, + category: "Online Shopping", + }, + }, + { + id: "7", + type: "Event", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.title; + }, + data: { + title: "AI in Healthcare Summit", + dateTime: { + start: "2025-07-15T09:00:00+01:00", + end: "2025-07-15T17:00:00+01:00", + }, + location: + "Royal College of Physicians, 11 St Andrews Pl, London NW1 4LE", + description: + "Annual conference exploring the latest developments in AI applications for healthcare", + organizer: { + name: "HealthTech Alliance", + email: "events@healthtechalliance.org", + }, + attendees: [ + "Dr. Sarah Chen", + "Prof. James Wilson", + "Dr. Maria Santos", + ], + category: "Conference", + }, + }, + { + id: "8", + type: "Contact", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.name; + }, + data: { + name: "Emma Schmidt", + phoneNumber: "+49 30 12345678", + emailAddress: "e.schmidt@techberlin.de", + address: "Friedrichstraße 123, 10117 Berlin, Germany", + organization: "TechBerlin GmbH", + title: "Chief Technology Officer", + notes: "Key contact for Berlin tech scene, met at TechFest 2024", + }, + }, + { + id: "9", + type: "Location", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.name; + }, + data: { + name: "Digital Nomad Hub", + address: "Calle Princesa 25, 08001 Barcelona, Spain", + category: "Coworking Space", + description: + "Modern coworking space with high-speed internet, meeting rooms, and a vibrant community of international remote workers", + }, + }, + { + id: "10", + type: "Website", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.title; + }, + data: { + url: "https://www.techcrunch.com", + title: "TechCrunch", + description: + "Leading technology media platform covering startups, tech news, and venture capital", + category: "Tech News", + }, + }, + { + id: "11", + type: "Note", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.title; + }, + data: { + title: "Product Roadmap 2025", + keywords: ["product development", "strategy", "features", "2025"], + content: + "## Overview\n\nPriority features for 2025:\n\n1. AI-powered customer insights dashboard\n2. Integration with major CRM platforms\n3. Mobile app redesign\n4. Enhanced analytics suite\n\n### Timeline\n- Q1: Research and planning\n- Q2: Development phase 1\n- Q3: Beta testing\n- Q4: Full release", + }, + }, + { + id: "12", + type: "Receipt", + get rawData() { + return getRawData(this); + }, + get title() { + return `${this.data.orderNumber} - ${this.data.vendor}`; + }, + data: { + receiptDate: "2025-03-15T13:45:00+01:00", + orderNumber: "INV789012", + amount: 1299.99, + currency: "EUR", + vendor: "Apple Store", + items: [ + { + name: "MacBook Air M3", + quantity: 1, + price: 1299.99, + currency: "EUR", + }, + ], + paymentMethod: "MasterCard", + shippingAddress: { + name: "Emma Schmidt", + address: "Friedrichstraße 123, 10117 Berlin, Germany", + }, + category: "Electronics", + }, + }, + { + id: "13", + type: "Event", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.title; + }, + data: { + title: "Sustainable Tech Workshop", + dateTime: { + start: "2025-08-20T14:00:00+02:00", + end: "2025-08-20T17:00:00+02:00", + }, + location: "GreenTech Hub, Prinsengracht 150, Amsterdam", + description: + "Workshop on implementing sustainable practices in tech companies", + organizer: { + name: "Green Digital Alliance", + email: "workshops@greendigital.org", + }, + attendees: ["Lisa van der Berg", "Mark Johnson"], + category: "Workshop", + }, + }, + { + id: "14", + type: "Contact", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.name; + }, + data: { + name: "Akiko Tanaka", + phoneNumber: "+81 3 1234 5678", + emailAddress: "a.tanaka@tokyotech.jp", + address: "2-1-1 Marunouchi, Chiyoda-ku, Tokyo 100-0005", + organization: "Tokyo Tech Ventures", + title: "Investment Director", + notes: "Specialist in Asia-Pacific tech investments", + }, + }, + { + id: "15", + type: "Location", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.name; + }, + data: { + name: "Innovation Center Stockholm", + address: "Regeringsgatan 65, 111 56 Stockholm, Sweden", + category: "Tech Hub", + description: + "Leading innovation center in Scandinavia, hosting startups and tech events", + }, + }, + { + id: "16", + type: "Website", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.title; + }, + data: { + url: "https://www.github.com", + title: "GitHub", + description: "World's leading software development platform", + category: "Development Tools", + }, + }, + { + id: "17", + type: "Note", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.title; + }, + data: { + title: "Team Structure Reorganization", + keywords: ["organization", "teams", "structure", "management"], + content: + "## Proposed Changes\n\n1. Create dedicated AI/ML team\n2. Merge frontend and mobile teams\n3. Establish DevOps center of excellence\n\n### Timeline\nGradual implementation over Q3 2025\n\n### Expected Outcomes\n- Improved efficiency\n- Better resource allocation\n- Faster delivery cycles", + }, + }, + { + id: "18", + type: "Receipt", + get rawData() { + return getRawData(this); + }, + get title() { + return `${this.data.orderNumber} - ${this.data.vendor}`; + }, + data: { + receiptDate: "2025-03-18T10:30:00+01:00", + orderNumber: "BOK456789", + amount: 850.0, + currency: "USD", + vendor: "Hilton Hotels", + items: [ + { + name: "Executive Suite - 2 nights", + quantity: 1, + price: 850.0, + currency: "USD", + }, + ], + paymentMethod: "American Express", + shippingAddress: { + name: "Akiko Tanaka", + address: + "Hilton San Francisco, 333 O'Farrell St, San Francisco, CA 94102", + }, + category: "Travel", + }, + }, + { + id: "19", + type: "Event", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.title; + }, + data: { + title: "Web3 Developer Conference", + dateTime: { + start: "2025-09-10T09:00:00-07:00", + end: "2025-09-12T17:00:00-07:00", + }, + location: "Moscone Center, 747 Howard St, San Francisco, CA 94103", + description: + "Three-day conference focusing on blockchain, DeFi, and Web3 development", + organizer: { + name: "Web3 Alliance", + email: "conference@web3alliance.org", + }, + attendees: ["Vitalik B.", "Charles H.", "Gavin W."], + category: "Conference", + }, + }, + { + id: "20", + type: "Contact", + get rawData() { + return getRawData(this); + }, + get title() { + return this.data.name; + }, + data: { + name: "Rachel Chen", + phoneNumber: "+1 415 555 0123", + emailAddress: "rachel.chen@siliconvc.com", + address: "525 Market St, San Francisco, CA 94105", + organization: "Silicon Valley Capital", + title: "Partner", + notes: "Specializes in Series A/B investments in AI and ML startups", + }, + }, +]; diff --git a/frontend/src/network/types.ts b/frontend/src/network/types.ts index 415cf8d..410584e 100644 --- a/frontend/src/network/types.ts +++ b/frontend/src/network/types.ts @@ -21,7 +21,10 @@ interface ReceiptItem { } interface DataItemType { + id: string; + title: string; type: T; + rawData: string; data: D; } diff --git a/frontend/src/utils/getCardSize.ts b/frontend/src/utils/getCardSize.ts new file mode 100644 index 0000000..f686815 --- /dev/null +++ b/frontend/src/utils/getCardSize.ts @@ -0,0 +1,20 @@ +export type CardSize = "1/1" | "2/1"; + +export const getCardSize = (type: string): CardSize => { + switch (type) { + case "Event": + return "2/1"; + case "Contact": + return "1/1"; + case "Location": + return "1/1"; + case "Note": + return "2/1"; + case "Website": + return "1/1"; + case "Receipt": + return "2/1"; + default: + return "1/1"; + } +}; diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 2aede78..045af77 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -4,7 +4,7 @@ export default { theme: { extend: { fontFamily: { - sans: ["Manrope", "sans-serif"], + sans: ["Switzer", "sans-serif"], }, }, },