fix all typescript and lint errors

main
Christopher Maujean 2 months ago
parent bc2d9bb842
commit a0bab787de
  1. 3
      CLAUDE.md
  2. 2
      src/.ldo/socialquery.typings.ts
  3. 9
      src/components/account/MyCollectionPage.tsx
  4. 5
      src/components/account/MyHomePage.tsx
  5. 6
      src/components/account/RCardPrivacySettings.tsx
  6. 4
      src/components/onboarding/BasicInfoStep.tsx
  7. 6
      src/components/onboarding/ConnectAccountsStep.tsx
  8. 34
      src/constants/onboarding.ts
  9. 45
      src/contexts/OnboardingContext.tsx
  10. 4
      src/contexts/OnboardingContextType.ts
  11. 10
      src/hooks/useOnboarding.ts
  12. 8
      src/lib/greencheck-api-client/index.ts
  13. 11
      src/pages/ContactListPage.tsx
  14. 16
      src/pages/GroupDetailPage.tsx
  15. 2
      src/pages/GroupJoinPage.tsx
  16. 2
      src/pages/SocialContractPage.tsx
  17. 44
      src/services/dataService.ts
  18. 8
      src/services/notificationService.ts
  19. 6
      src/types/nextgraph.ts
  20. 4
      src/types/notification.ts

@ -39,4 +39,5 @@ This is a React/TypeScript NAO (Network of Authentic Others) community managemen
- Formatting: eslint & prettier with 2-space indentation, 100 char line width
- Components: Functional components with named exports
- Naming: PascalCase for components/types, camelCase for variables/functions
- Comments: No comments about changes or removals, only absolutely necessary comments in any case.
- Comments: No comments about changes or removals, only absolutely necessary comments in any case.
- YOU MUST FOLLOW ALL ESLINT RULES, NO EXCEPTIONS.

@ -1,4 +1,4 @@
import { LdoJsonldContext, LdSet } from "@ldo/ldo";
import { LdoJsonldContext } from "@ldo/ldo";
/**
* =============================================================================

@ -46,11 +46,8 @@ import {
} from '@mui/icons-material';
import type { BookmarkedItem, Collection, CollectionFilter, CollectionStats } from '@/types/collection';
interface MyCollectionPageProps {
// Props would come from parent component
}
const MyCollectionPage = ({}: MyCollectionPageProps) => {
const MyCollectionPage = () => {
const theme = useTheme();
const [items, setItems] = useState<BookmarkedItem[]>([]);
const [collections, setCollections] = useState<Collection[]>([]);
@ -63,7 +60,7 @@ const MyCollectionPage = ({}: MyCollectionPageProps) => {
const [menuAnchor, setMenuAnchor] = useState<{ [key: string]: HTMLElement | null }>({});
const [showQueryDialog, setShowQueryDialog] = useState(false);
const [queryText, setQueryText] = useState('');
const [/* stats */] = useState<CollectionStats>({
const [/* stats */, /* setStats */] = useState<CollectionStats>({
totalItems: 0,
unreadItems: 0,
favoriteItems: 0,
@ -255,7 +252,7 @@ const MyCollectionPage = ({}: MyCollectionPageProps) => {
// Filter by collection
if (selectedCollection !== 'all') {
// In a real implementation, this would filter by collection membership
filtered = filtered; // For now, show all items
// For now, show all items - no filtering needed
}
// Filter by category

@ -36,11 +36,8 @@ import {
} from '@mui/icons-material';
import type { UserContent, ContentFilter, ContentStats, ContentType } from '@/types/userContent';
interface MyHomePageProps {
// Props would come from parent component
}
const MyHomePage = ({}: MyHomePageProps) => {
const MyHomePage = () => {
const [content, setContent] = useState<UserContent[]>([]);
const [filteredContent, setFilteredContent] = useState<UserContent[]>([]);
const [filter] = useState<ContentFilter>({});

@ -40,7 +40,7 @@ const RCardPrivacySettings = ({ rCard, onUpdate }: RCardPrivacySettingsProps) =>
const handleSettingChange = (
category: string,
field: string,
value: any
value: unknown
) => {
const newSettings = { ...settings };
@ -57,9 +57,9 @@ const RCardPrivacySettings = ({ rCard, onUpdate }: RCardPrivacySettingsProps) =>
} else if (category === 'general') {
// Handle root level properties
if (field === 'keyRecoveryBuddy' || field === 'circlesTrustedConnection') {
(newSettings as any)[field] = value;
(newSettings as Record<string, unknown>)[field] = value;
} else if (field === 'locationSharing' || field === 'locationDeletionHours') {
(newSettings as any)[field] = value;
(newSettings as Record<string, unknown>)[field] = value;
}
}

@ -10,7 +10,7 @@ import {
InputAdornment
} from '@mui/material';
import { Person, Email, Phone, Business, Work, PhotoCamera } from '@mui/icons-material';
import { useOnboarding } from '@/contexts/OnboardingContext';
import { useOnboarding } from '@/hooks/useOnboarding';
const BasicInfoStep = () => {
const { state, updateProfile } = useOnboarding();
@ -55,7 +55,7 @@ const BasicInfoStep = () => {
}
break;
case 'phone':
if (value && !/^\+?[\d\s\-\(\)]+$/.test(value)) {
if (value && !/^\+?[\d\s\-()]+$/.test(value)) {
newErrors[field] = 'Please enter a valid phone number';
}
break;

@ -27,14 +27,14 @@ import {
Cloud,
Security
} from '@mui/icons-material';
import { useOnboarding } from '@/contexts/OnboardingContext';
import { useOnboarding } from '@/hooks/useOnboarding';
const ConnectAccountsStep = () => {
const { state, connectAccount, disconnectAccount } = useOnboarding();
const [connectingAccount, setConnectingAccount] = useState<string | null>(null);
const [connectionDialog, setConnectionDialog] = useState<{
open: boolean;
account: any;
account: { id: string; type: string; isConnected: boolean; [key: string]: unknown; } | null;
action: 'connect' | 'disconnect';
}>({
open: false,
@ -72,7 +72,7 @@ const ConnectAccountsStep = () => {
}
};
const handleConnectionToggle = (account: any) => {
const handleConnectionToggle = (account: { id: string; type: string; isConnected: boolean; [key: string]: unknown; }) => {
if (account.isConnected) {
setConnectionDialog({
open: true,

@ -0,0 +1,34 @@
import type { OnboardingState } from '@/types/onboarding';
export const initialState: OnboardingState = {
currentStep: 0,
totalSteps: 2,
userProfile: {},
connectedAccounts: [
{
id: 'linkedin',
type: 'linkedin',
name: 'LinkedIn',
isConnected: false,
},
{
id: 'contacts',
type: 'contacts',
name: 'Contacts',
isConnected: false,
},
{
id: 'google',
type: 'google',
name: 'Google',
isConnected: false,
},
{
id: 'apple',
type: 'apple',
name: 'Apple',
isConnected: false,
},
],
isComplete: false,
};

@ -1,39 +1,8 @@
import { createContext, useContext, useReducer } from 'react';
import { useReducer } from 'react';
import type { ReactNode } from 'react';
import type { OnboardingState, OnboardingContextType, UserProfile } from '@/types/onboarding';
const initialState: OnboardingState = {
currentStep: 0,
totalSteps: 2,
userProfile: {},
connectedAccounts: [
{
id: 'linkedin',
type: 'linkedin',
name: 'LinkedIn',
isConnected: false,
},
{
id: 'contacts',
type: 'contacts',
name: 'Contacts',
isConnected: false,
},
{
id: 'google',
type: 'google',
name: 'Google',
isConnected: false,
},
{
id: 'apple',
type: 'apple',
name: 'Apple',
isConnected: false,
},
],
isComplete: false,
};
import { OnboardingContext } from '@/contexts/OnboardingContextType';
import { initialState } from '@/constants/onboarding';
type OnboardingAction =
| { type: 'UPDATE_PROFILE'; payload: Partial<UserProfile> }
@ -98,7 +67,6 @@ const onboardingReducer = (state: OnboardingState, action: OnboardingAction): On
}
};
const OnboardingContext = createContext<OnboardingContextType | undefined>(undefined);
export const OnboardingProvider = ({ children }: { children: ReactNode }) => {
const [state, dispatch] = useReducer(onboardingReducer, initialState);
@ -144,10 +112,3 @@ export const OnboardingProvider = ({ children }: { children: ReactNode }) => {
);
};
export const useOnboarding = () => {
const context = useContext(OnboardingContext);
if (context === undefined) {
throw new Error('useOnboarding must be used within an OnboardingProvider');
}
return context;
};

@ -0,0 +1,4 @@
import { createContext } from 'react';
import type { OnboardingContextType } from '@/types/onboarding';
export const OnboardingContext = createContext<OnboardingContextType | undefined>(undefined);

@ -0,0 +1,10 @@
import { useContext } from 'react';
import { OnboardingContext } from '@/contexts/OnboardingContextType';
export const useOnboarding = () => {
const context = useContext(OnboardingContext);
if (context === undefined) {
throw new Error('useOnboarding must be used within an OnboardingProvider');
}
return context;
};

@ -23,10 +23,12 @@ function getGlobalFetch(): typeof fetch {
if (typeof global !== 'undefined' && (global as Record<string, unknown>).fetch) {
return ((global as Record<string, unknown>).fetch as typeof fetch).bind(global);
}
// For Node.js environments without fetch polyfill
// For Node.js environments without fetch polyfill - use dynamic import
let nodeFetch: typeof fetch;
try {
const nodeFetch = require('node-fetch');
return nodeFetch.default || nodeFetch;
// eslint-disable-next-line @typescript-eslint/no-require-imports
nodeFetch = require('node-fetch');
return nodeFetch;
} catch {
throw new Error('No fetch implementation found. Please install node-fetch for Node.js environments.');
}

@ -105,7 +105,7 @@ const ContactListPage = () => {
}, []);
useEffect(() => {
let filtered = contacts.filter(contact => {
const filtered = contacts.filter(contact => {
// Search filter
const matchesSearch = searchQuery === '' ||
contact.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
@ -134,21 +134,24 @@ const ContactListPage = () => {
case 'name':
compareValue = a.name.localeCompare(b.name);
break;
case 'company':
case 'company': {
const aCompany = a.company || '';
const bCompany = b.company || '';
compareValue = aCompany.localeCompare(bCompany);
break;
case 'naoStatus':
}
case 'naoStatus': {
const statusOrder = { 'member': 0, 'invited': 1, 'not_invited': 2 };
compareValue = (statusOrder[a.naoStatus as keyof typeof statusOrder] || 3) -
(statusOrder[b.naoStatus as keyof typeof statusOrder] || 3);
break;
case 'groupCount':
}
case 'groupCount': {
const aGroups = a.groupIds?.length || 0;
const bGroups = b.groupIds?.length || 0;
compareValue = aGroups - bGroups;
break;
}
default:
compareValue = 0;
}

@ -261,7 +261,7 @@ const GroupDetailPage = () => {
};
loadGroupData();
}, [groupId]);
}, [groupId, searchParams, setSearchParams]);
// Set initial prompt when provided
useEffect(() => {
@ -674,11 +674,11 @@ const GroupDetailPage = () => {
const members = getMockMembers();
// Get all unique people and topics for filters
const allPeople = ['all', ...Array.from(new Set(posts.map((p: any) => p.authorName)))];
const allTopics = ['all', ...Array.from(new Set(posts.map((p: any) => p.topic).filter(Boolean)))];
const allPeople = ['all', ...Array.from(new Set(posts.map((p) => p.authorName)))];
const allTopics = ['all', ...Array.from(new Set(posts.map((p) => (p as { topic?: string }).topic).filter(Boolean)))];
// Filter posts based on selected filters
const filteredPosts = posts.filter((post: any) => {
const filteredPosts = posts.filter((post) => {
const personMatch = selectedPersonFilter === 'all' || post.authorName === selectedPersonFilter;
const topicMatch = selectedTopicFilter === 'all' || post.topic === selectedTopicFilter;
return personMatch && topicMatch;
@ -694,7 +694,7 @@ const GroupDetailPage = () => {
setExpandedPosts(newExpanded);
};
const renderPost = (post: any) => {
const renderPost = (post: GroupPost & { topic?: string; images?: string[]; isLong?: boolean }) => {
const isExpanded = expandedPosts.has(post.id);
const isLongPost = post.isLong;
const shouldTruncate = isLongPost && !isExpanded;
@ -1276,9 +1276,9 @@ const GroupDetailPage = () => {
const renderNetworkView = (members: any[]) => {
const renderNetworkView = (members: Array<{ id: string; name: string; avatar?: string; [key: string]: unknown; }>) => {
// Shared position calculation function for perfect alignment
const getNodePosition = (member: any) => {
const getNodePosition = (member: { id: string; name: string; [key: string]: unknown; }) => {
const centerX = 400; // Center of 800px viewBox
const centerY = 400; // Center of 800px viewBox
const scale = 1.8; // Increased scale for better visibility
@ -1486,7 +1486,7 @@ const GroupDetailPage = () => {
};
const renderMapView = (members: any[], compact?: boolean) => {
const renderMapView = (members: Array<{ id: string; name: string; location?: string; [key: string]: unknown; }>, compact?: boolean) => {
const visibleMembers = members.filter(m => m.location?.visible);
return (

@ -36,7 +36,7 @@ const GroupJoinPage = () => {
const [selectedProfileCard, setSelectedProfileCard] = useState<string>('');
const [inviterName, setInviterName] = useState<string>('');
const [isLoading, setIsLoading] = useState(true);
const [customProfileCard, setCustomProfileCard] = useState<any>(null);
const [customProfileCard, setCustomProfileCard] = useState<{ id: string; name: string; [key: string]: unknown; } | null>(null);
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const theme = useTheme();

@ -97,7 +97,7 @@ const SocialContractPage = () => {
};
loadGroupData();
}, [searchParams]);
}, [searchParams, navigate]);
const handleAccept = () => {
// Store acceptance in session

@ -3,12 +3,12 @@ import type { Group } from '@/types/group';
export const dataService = {
async getContacts(): Promise<Contact[]> {
return new Promise(async (resolve) => {
return new Promise((resolve) => {
setTimeout(async () => {
try {
const response = await fetch('/contacts.json');
const contactsData = await response.json();
const contacts = contactsData.map((contact: any) => {
const contacts = contactsData.map((contact: Contact & { createdAt: string; updatedAt: string; joinedAt?: string; invitedAt?: string; }) => {
const processedContact = {
...contact,
createdAt: new Date(contact.createdAt),
@ -35,12 +35,12 @@ export const dataService = {
},
async getContact(id: string): Promise<Contact | undefined> {
return new Promise(async (resolve) => {
return new Promise((resolve) => {
setTimeout(async () => {
try {
const response = await fetch('/contacts.json');
const contactsData = await response.json();
const contact = contactsData.find((c: any) => c.id === id);
const contact = contactsData.find((c: Contact) => c.id === id);
if (contact) {
const processedContact = {
...contact,
@ -69,7 +69,7 @@ export const dataService = {
},
async getImportSources(): Promise<ImportSource[]> {
return new Promise(async (resolve) => {
return new Promise((resolve) => {
setTimeout(async () => {
try {
const response = await fetch('/import-sources.json');
@ -84,14 +84,14 @@ export const dataService = {
},
async importFromSource(sourceId: string): Promise<Contact[]> {
return new Promise(async (resolve) => {
return new Promise((resolve) => {
setTimeout(async () => {
try {
const response = await fetch('/contacts.json');
const contactsData = await response.json();
const filteredContacts = contactsData
.filter((contact: any) => contact.source === sourceId)
.map((contact: any) => ({
.filter((contact: Contact) => contact.source === sourceId)
.map((contact: Contact) => ({
...contact,
createdAt: new Date(contact.createdAt),
updatedAt: new Date(contact.updatedAt)
@ -106,14 +106,14 @@ export const dataService = {
},
async getGroups(): Promise<Group[]> {
return new Promise(async (resolve) => {
return new Promise((resolve) => {
setTimeout(async () => {
try {
const response = await fetch('/groups.json');
const groupsData = await response.json();
const groups = groupsData.map((group: any) => {
const groups = groupsData.map((group: Group & { createdAt: string; updatedAt: string; latestPostAt?: string; }) => {
const processedGroup = {
...group,
...(group as unknown as Group),
createdAt: new Date(group.createdAt),
updatedAt: new Date(group.updatedAt)
};
@ -135,15 +135,15 @@ export const dataService = {
},
async getGroup(id: string): Promise<Group | undefined> {
return new Promise(async (resolve) => {
return new Promise((resolve) => {
setTimeout(async () => {
try {
const response = await fetch('/groups.json');
const groupsData = await response.json();
const group = groupsData.find((g: any) => g.id === id);
const group = groupsData.find((g: Group) => g.id === id);
if (group) {
const processedGroup = {
...group,
...(group as unknown as Group),
createdAt: new Date(group.createdAt),
updatedAt: new Date(group.updatedAt)
};
@ -166,23 +166,23 @@ export const dataService = {
},
async getGroupsForUser(userId: string): Promise<Group[]> {
return new Promise(async (resolve) => {
return new Promise((resolve) => {
setTimeout(async () => {
try {
const response = await fetch('/groups.json');
const groupsData = await response.json();
const userGroups = groupsData
.filter((group: any) => group.memberIds.includes(userId))
.map((group: any) => {
const processedGroup = {
...group,
createdAt: new Date(group.createdAt),
updatedAt: new Date(group.updatedAt)
.filter((group: Record<string, unknown>) => (group.memberIds as string[]).includes(userId))
.map((group: Record<string, unknown>) => {
const processedGroup: Group = {
...(group as unknown as Group),
createdAt: new Date(group.createdAt as string),
updatedAt: new Date(group.updatedAt as string)
};
// Convert optional date fields if they exist
if (group.latestPostAt) {
processedGroup.latestPostAt = new Date(group.latestPostAt);
processedGroup.latestPostAt = new Date(group.latestPostAt as string);
}
return processedGroup;

@ -196,7 +196,7 @@ export class NotificationService {
}
// Accept a vouch
async acceptVouch(notificationId: string, _vouchId: string): Promise<void> {
async acceptVouch(notificationId: string): Promise<void> {
await new Promise(resolve => setTimeout(resolve, 400));
const notification = this.notifications.find(n => n.id === notificationId);
if (notification) {
@ -207,7 +207,7 @@ export class NotificationService {
}
// Reject a vouch
async rejectVouch(notificationId: string, _vouchId: string): Promise<void> {
async rejectVouch(notificationId: string): Promise<void> {
await new Promise(resolve => setTimeout(resolve, 400));
const notification = this.notifications.find(n => n.id === notificationId);
if (notification) {
@ -218,7 +218,7 @@ export class NotificationService {
}
// Accept praise
async acceptPraise(notificationId: string, _praiseId: string): Promise<void> {
async acceptPraise(notificationId: string): Promise<void> {
await new Promise(resolve => setTimeout(resolve, 400));
const notification = this.notifications.find(n => n.id === notificationId);
if (notification) {
@ -229,7 +229,7 @@ export class NotificationService {
}
// Reject praise
async rejectPraise(notificationId: string, _praiseId: string): Promise<void> {
async rejectPraise(notificationId: string): Promise<void> {
await new Promise(resolve => setTimeout(resolve, 400));
const notification = this.notifications.find(n => n.id === notificationId);
if (notification) {

@ -1,12 +1,12 @@
export interface NextGraphSession {
ng?: any;
ng?: unknown;
privateStoreId?: string;
[key: string]: any;
[key: string]: unknown;
}
export interface NextGraphAuth {
session?: NextGraphSession;
login?: () => void;
logout?: () => void;
[key: string]: any;
[key: string]: unknown;
}

@ -10,7 +10,7 @@ export interface ProfileCard {
}
// Legacy alias for backwards compatibility
export interface RCard extends ProfileCard {}
export type RCard = ProfileCard;
export interface Vouch {
id: string;
@ -130,7 +130,7 @@ export interface ProfileCardWithPrivacy extends ProfileCard {
}
// Legacy alias for backwards compatibility
export interface RCardWithPrivacy extends ProfileCardWithPrivacy {}
export type RCardWithPrivacy = ProfileCardWithPrivacy;
export interface ContactPrivacyOverride {
contactId: string;

Loading…
Cancel
Save