diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2c80a56 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,187 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +**SUMilanApp** is a web application for the SingularityU Milan Chapter that enhances event attendance experiences. It provides event information, live streaming, NFT certificate minting, and multilingual support (English/Italian). + +**Live version**: https://app.singularityumilan.com + +## Architecture + +### Monorepo Structure (Nx Workspace) +- **`apps/sumilan-app/`**: Main Ionic React application +- **`apps/sumilan-app-e2e/`**: Cypress end-to-end tests +- **`apps/smart-contract-api/`**: Smart contract API backend +- **`apps/notify-activist-request/`**: Notification service +- **`libs/api/`**: GraphQL API client library +- **`libs/smart-contract/`**: Solidity smart contracts with Hardhat +- **`libs/eventbrite-wrapper/`**: Eventbrite API integration + +### Technology Stack +- **Frontend**: Ionic + React + TypeScript +- **Backend**: Supabase (database + authentication) +- **Blockchain**: Ethereum smart contracts (Hardhat + Solidity 0.8.9) +- **Mobile**: Capacitor for iOS/Android deployment +- **Monorepo**: Nx workspace management +- **Testing**: Jest + Cypress + Hardhat tests + +### Key Features +- Event management with live streaming +- NFT certificate minting for attendance +- Multi-language support (i18n) +- Progressive Web App capabilities +- Eventbrite integration for ticket validation +- Blockchain-based certificate verification + +## Development Commands + +### Core Development +```bash +# Install dependencies +npm install + +# Start development server (with SSL and HMR) +npx nx run sumilan-app:serve + +# Build for production +npx nx run sumilan-app:build:production + +# Run tests +npx nx run sumilan-app:test + +# Run linting +npx nx run sumilan-app:lint + +# Run all tests in workspace +npm test +``` + +### Mobile Development (Capacitor) +```bash +# Add mobile platforms +npx nx run sumilan-app:add:ios +npx nx run sumilan-app:add:android + +# Sync web assets to mobile +npx nx run sumilan-app:sync:ios +npx nx run sumilan-app:sync:android + +# Open in native IDE +npx nx run sumilan-app:open:ios +npx nx run sumilan-app:open:android +``` + +### Smart Contract Development +```bash +# Navigate to smart contract directory +cd libs/smart-contract + +# Compile contracts +npx hardhat compile + +# Run contract tests +npx hardhat test + +# Deploy to test network (Ropsten) +npx hardhat run scripts/deploy.js --network ropsten +``` + +## Environment Setup + +Required environment variables in `.env`: +``` +NX_SUPABASE_URL="your_supabase_url" +NX_SUPABASE_GRAPHQL_ENDPOINT="your_supabase_url/graphql/v1" +NX_SUPABASE_PUBLIC_KEY="your_supabase_public_key" +NX_IBM_EVENTBRITE_WRAPPER_KEY="your_ibm_cloud_function_key" +NX_AWS_LAMBDA_CERTIFICATE_API_KEY="your_aws_lambda_api_key" +ALCHEMY_API_URL="your_alchemy_api_url" +METAMASK_PRIVATE_KEY="your_metamask_private_key" +``` + +## Code Architecture + +### Path Mappings (tsconfig.base.json) +- `@sumilan-app/api` → `libs/api/src/index.ts` +- `@sumilan-app/eventbrite-wrapper` → `libs/eventbrite-wrapper/src/index.js` +- `@sumilan-app/smart-contract` → `libs/smart-contract/src/index.js` + +### Main App Structure +- **`/app/components/`**: Reusable React components +- **`/app/pages/`**: Page-level components with routing +- **`/app/services/`**: API integrations and business logic +- **`/app/store/`**: Redux Toolkit state management +- **`/app/contexts/`**: React contexts for global state +- **`/app/utils/`**: Utility functions and helpers +- **`/assets/i18n/`**: Internationalization files (en.json, it.json) + +### Key Services +- **`supabase.ts`**: Database and authentication client +- **`web3.ts`**: Web3 blockchain integration +- **`eventbriteWrapper.ts`**: Eventbrite API integration +- **`mintApi.ts`**: NFT certificate minting service +- **`SUMilanCertificate.ts`**: Smart contract interaction + +### State Management +- **Redux Toolkit** for global state +- **React Context** for authentication and video player state +- **React Hook Form** for form management + +## Testing + +### Unit Tests (Jest) +```bash +# Run specific app tests +npx nx run sumilan-app:test + +# Run specific lib tests +npx nx run api:test +npx nx run smart-contract:test +``` + +### E2E Tests (Cypress) +```bash +# Run e2e tests +npx nx run sumilan-app-e2e:e2e +``` + +### Smart Contract Tests (Hardhat) +```bash +cd libs/smart-contract +npx hardhat test +``` + +## Deployment + +### Web Deployment +Build output is generated in `dist/apps/sumilan-app/` ready for static hosting. + +### Mobile Deployment +1. Build web assets: `npx nx run sumilan-app:build:production` +2. Sync to mobile: `npx nx run sumilan-app:sync:ios` or `npx nx run sumilan-app:sync:android` +3. Open in Xcode/Android Studio for final build and deployment + +## Important Notes + +### Development Server +- Uses SSL by default (`ssl: true` in serve options) +- Hot Module Replacement (HMR) enabled for fast development +- Serves on HTTPS for testing PWA features and Web3 integration + +### Blockchain Integration +- Smart contracts deployed on Ropsten testnet +- Uses Hardhat for development and testing +- OpenZeppelin contracts for security +- Upgradeable contract pattern implemented + +### Internationalization +- Files: `assets/i18n/en.json` and `assets/i18n/it.json` +- Uses `react-i18next` for translation management +- Automatic language detection based on browser settings + +### Performance +- Nx caching enabled for build, lint, test, and e2e operations +- PWA with service worker for offline capabilities +- Optimized production builds with code splitting \ No newline at end of file diff --git a/apps/sumilan-app/src/app/components/EventTime/EventTimeComponent.tsx b/apps/sumilan-app/src/app/components/EventTime/EventTimeComponent.tsx index 63bc65a..e506414 100644 --- a/apps/sumilan-app/src/app/components/EventTime/EventTimeComponent.tsx +++ b/apps/sumilan-app/src/app/components/EventTime/EventTimeComponent.tsx @@ -18,7 +18,7 @@ import './EventTimeComponent.css'; const dateFormat = Object.assign({}, DateTime.DATETIME_MED); interface EventTimeProps { - date: string; + date: string | null | undefined; duration: number; // in minutes } @@ -33,7 +33,7 @@ const TimeContainer: React.FC = ({ children }) => { }; const EventTimeComponent: React.FC = ({ date, duration }) => { - const dt = DateTime.fromISO(date); + const dt = date ? DateTime.fromISO(date) : DateTime.now(); const { timeStatus: eventTimeStatus } = useContext(EventTimeContext) as EventTimeContextModel; const { t } = useTranslation(); diff --git a/apps/sumilan-app/src/app/contexts/EventTime.tsx b/apps/sumilan-app/src/app/contexts/EventTime.tsx index 76a969b..fa8c99d 100644 --- a/apps/sumilan-app/src/app/contexts/EventTime.tsx +++ b/apps/sumilan-app/src/app/contexts/EventTime.tsx @@ -8,12 +8,12 @@ import { Event_Time_Status } from "@sumilan-app/api"; export const EventTimeContext = createContext(null); type Props = { - date: string; + date: string | null | undefined; duration: number; }; export const EventTimeProvider: React.FC = ({ children, date, duration }) => { - const dt = DateTime.fromISO(date); + const dt = date ? DateTime.fromISO(date) : DateTime.now(); const [timeStatus, setTimeStatus] = useState(getEventTimeStatus(dt, duration)); useEffect(() => { diff --git a/apps/sumilan-app/src/app/models/activist-request.model.ts b/apps/sumilan-app/src/app/models/activist-request.model.ts index a3e0380..b67d6a8 100644 --- a/apps/sumilan-app/src/app/models/activist-request.model.ts +++ b/apps/sumilan-app/src/app/models/activist-request.model.ts @@ -1,5 +1,5 @@ export interface ActivistRequest { - id: number; + id: string; email: string; accepted: boolean; created_at: string; diff --git a/apps/sumilan-app/src/app/pages/Home/Home.tsx b/apps/sumilan-app/src/app/pages/Home/Home.tsx index e7934e3..8e10868 100644 --- a/apps/sumilan-app/src/app/pages/Home/Home.tsx +++ b/apps/sumilan-app/src/app/pages/Home/Home.tsx @@ -31,13 +31,13 @@ import { Events, Event_Time_Status, useGetEventsQuery } from '@sumilan-app/api'; import './Home.css'; interface GroupedEventsModel { - passed: Partial[]; - scheduled: Partial[]; - today: Partial[]; + passed: Events[]; + scheduled: Events[]; + today: Events[]; } interface EventsListComponentWithRouteProps extends RouteComponentProps { - events: Partial[]; + events: Events[]; title: string; } @@ -128,17 +128,11 @@ const Home: React.FC = (props: RouteComponentProps) => { ev?.event_description?.toLowerCase().includes(searchQuery) )) { if (ev?.event_time_status === Event_Time_Status.Passed) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - groupedEvents.passed.push(ev); + groupedEvents.passed.push(ev as Events); } else if (ev?.event_time_status === Event_Time_Status.Scheduled) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - groupedEvents.scheduled.push(ev); + groupedEvents.scheduled.push(ev as Events); } else { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - groupedEvents.today.push(ev); + groupedEvents.today.push(ev as Events); } } } diff --git a/graphql.config.json b/graphql.config.json index 0203b62..9c53a21 100644 --- a/graphql.config.json +++ b/graphql.config.json @@ -20,6 +20,15 @@ "typescript-urql" ], "config": { + "scalars": { + "BigInt": "string", + "Cursor": "string", + "Date": "string", + "Datetime": "string", + "JSON": "Record", + "Time": "string", + "UUID": "string" + }, "skipTypeNameForRoot": true, "skipTypename": true, "withHooks": true, diff --git a/libs/api/src/lib/generated/generated.ts b/libs/api/src/lib/generated/generated.ts index 15ce52f..b2623bf 100644 --- a/libs/api/src/lib/generated/generated.ts +++ b/libs/api/src/lib/generated/generated.ts @@ -13,13 +13,13 @@ export type Scalars = { Boolean: boolean; Int: number; Float: number; - BigInt: any; - Cursor: any; - Date: any; - Datetime: any; - JSON: any; - Time: any; - UUID: any; + BigInt: string; + Cursor: string; + Date: string; + Datetime: string; + JSON: Record; + Time: string; + UUID: string; }; /** Boolean expression comparing fields on type "BigInt" */ diff --git a/package-lock.json b/package-lock.json index f63d6cd..949273b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@reduxjs/toolkit": "^1.8.0", "@supabase/supabase-js": "^1.32.1", "animate.css": "^4.1.1", + "caniuse-lite": "^1.0.30001726", "core-js": "^3.6.5", "date-fns": "^2.28.0", "dotenv": "^16.0.0", @@ -10719,10 +10720,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001320", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz", - "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==", - "dev": true, + "version": "1.0.30001726", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", + "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", "funding": [ { "type": "opencollective", @@ -10731,8 +10731,13 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/capital-case": { "version": "1.0.4", @@ -47666,10 +47671,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001320", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz", - "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==", - "dev": true + "version": "1.0.30001726", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", + "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==" }, "capital-case": { "version": "1.0.4", diff --git a/package.json b/package.json index 848677c..b215324 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "@reduxjs/toolkit": "^1.8.0", "@supabase/supabase-js": "^1.32.1", "animate.css": "^4.1.1", + "caniuse-lite": "^1.0.30001726", "core-js": "^3.6.5", "date-fns": "^2.28.0", "dotenv": "^16.0.0",