refactor: convert frontend from submodule to true monorepo
Convert frontend from Git submodule to a regular monorepo directory for simplified development workflow. Changes: - Remove frontend submodule tracking (mode 160000 gitlink) - Add all frontend source files directly to main repository - Remove frontend/.git directory - Update CLAUDE.md to clarify true monorepo structure - Update Frontend Architecture documentation (React Router v6, Socket.IO, Leaflet, ErrorBoundary) Benefits of Monorepo: - Single git clone for entire project - Unified commit history - Simpler CI/CD pipeline - Easier for new developers - No submodule sync issues - Atomic commits across frontend and backend Frontend Files Added: - All React components (MapView, ErrorBoundary, TaskList, SocialFeed, etc.) - Context providers (AuthContext, SocketContext) - Complete test suite with MSW - Dependencies and configuration files Branch Cleanup: - Using 'main' as default branch (develop deleted) - Frontend no longer has separate Git history 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,396 @@
|
||||
import { http, HttpResponse } from 'msw';
|
||||
|
||||
// Mock API base URL (matches the proxy in package.json)
|
||||
const API_URL = 'http://localhost:5000';
|
||||
|
||||
// Mock data
|
||||
const mockUser = {
|
||||
_id: 'user123',
|
||||
name: 'Test User',
|
||||
email: 'test@example.com',
|
||||
isPremium: false,
|
||||
points: 100,
|
||||
adoptedStreets: [],
|
||||
completedTasks: [],
|
||||
posts: [],
|
||||
events: [],
|
||||
};
|
||||
|
||||
const mockStreets = [
|
||||
{
|
||||
_id: 'street1',
|
||||
name: 'Main Street',
|
||||
city: 'Test City',
|
||||
state: 'TS',
|
||||
location: {
|
||||
type: 'Point',
|
||||
coordinates: [-73.935242, 40.730610],
|
||||
},
|
||||
adoptedBy: 'user123',
|
||||
status: 'active',
|
||||
},
|
||||
{
|
||||
_id: 'street2',
|
||||
name: 'Oak Avenue',
|
||||
city: 'Test City',
|
||||
state: 'TS',
|
||||
location: {
|
||||
type: 'Point',
|
||||
coordinates: [-73.945242, 40.740610],
|
||||
},
|
||||
adoptedBy: 'user456',
|
||||
status: 'active',
|
||||
},
|
||||
];
|
||||
|
||||
const mockTasks = [
|
||||
{
|
||||
_id: 'task1',
|
||||
street: 'street1',
|
||||
description: 'Clean up litter',
|
||||
type: 'cleaning',
|
||||
status: 'pending',
|
||||
createdBy: 'user123',
|
||||
},
|
||||
{
|
||||
_id: 'task2',
|
||||
street: 'street1',
|
||||
description: 'Fix pothole',
|
||||
type: 'repair',
|
||||
status: 'completed',
|
||||
createdBy: 'user123',
|
||||
},
|
||||
];
|
||||
|
||||
const mockPosts = [
|
||||
{
|
||||
_id: 'post1',
|
||||
user: {
|
||||
_id: 'user123',
|
||||
name: 'Test User',
|
||||
profilePicture: null,
|
||||
},
|
||||
content: 'Just cleaned up Main Street!',
|
||||
type: 'text',
|
||||
likes: [],
|
||||
comments: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
_id: 'post2',
|
||||
user: {
|
||||
_id: 'user456',
|
||||
name: 'Another User',
|
||||
profilePicture: null,
|
||||
},
|
||||
content: 'Great work everyone!',
|
||||
type: 'text',
|
||||
likes: ['user123'],
|
||||
comments: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
const mockEvents = [
|
||||
{
|
||||
_id: 'event1',
|
||||
title: 'Community Cleanup',
|
||||
description: 'Annual community cleanup event',
|
||||
date: new Date(Date.now() + 86400000).toISOString(),
|
||||
location: 'Central Park',
|
||||
organizer: 'user123',
|
||||
participants: [],
|
||||
},
|
||||
{
|
||||
_id: 'event2',
|
||||
title: 'Tree Planting Day',
|
||||
description: 'Help us plant trees in the neighborhood',
|
||||
date: new Date(Date.now() + 172800000).toISOString(),
|
||||
location: 'Riverside Park',
|
||||
organizer: 'user456',
|
||||
participants: ['user123'],
|
||||
},
|
||||
];
|
||||
|
||||
const mockRewards = [
|
||||
{
|
||||
_id: 'reward1',
|
||||
name: 'Bronze Badge',
|
||||
description: 'Complete 5 tasks',
|
||||
pointsCost: 50,
|
||||
isPremium: false,
|
||||
},
|
||||
{
|
||||
_id: 'reward2',
|
||||
name: 'Silver Badge',
|
||||
description: 'Complete 20 tasks',
|
||||
pointsCost: 150,
|
||||
isPremium: false,
|
||||
},
|
||||
{
|
||||
_id: 'reward3',
|
||||
name: 'Premium Badge',
|
||||
description: 'Exclusive premium badge',
|
||||
pointsCost: 200,
|
||||
isPremium: true,
|
||||
},
|
||||
];
|
||||
|
||||
// Auth handlers
|
||||
const authHandlers = [
|
||||
// Register
|
||||
http.post(`${API_URL}/api/auth/register`, async ({ request }) => {
|
||||
const body = await request.json();
|
||||
return HttpResponse.json({
|
||||
token: 'mock-jwt-token',
|
||||
});
|
||||
}),
|
||||
|
||||
// Login
|
||||
http.post(`${API_URL}/api/auth/login`, async ({ request }) => {
|
||||
const body = await request.json();
|
||||
|
||||
if (body.email === 'test@example.com' && body.password === 'password123') {
|
||||
return HttpResponse.json({
|
||||
token: 'mock-jwt-token',
|
||||
});
|
||||
}
|
||||
|
||||
return HttpResponse.json(
|
||||
{ msg: 'Invalid credentials' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}),
|
||||
|
||||
// Get authenticated user
|
||||
http.get(`${API_URL}/api/auth`, ({ request }) => {
|
||||
const token = request.headers.get('x-auth-token');
|
||||
|
||||
if (!token) {
|
||||
return HttpResponse.json(
|
||||
{ msg: 'No token, authorization denied' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
return HttpResponse.json(mockUser);
|
||||
}),
|
||||
];
|
||||
|
||||
// Streets handlers
|
||||
const streetsHandlers = [
|
||||
// Get all streets
|
||||
http.get(`${API_URL}/api/streets`, () => {
|
||||
return HttpResponse.json(mockStreets);
|
||||
}),
|
||||
|
||||
// Adopt a street
|
||||
http.put(`${API_URL}/api/streets/adopt/:id`, ({ params }) => {
|
||||
const street = mockStreets.find(s => s._id === params.id);
|
||||
|
||||
if (!street) {
|
||||
return HttpResponse.json(
|
||||
{ msg: 'Street not found' },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
return HttpResponse.json({
|
||||
...street,
|
||||
adoptedBy: 'user123',
|
||||
});
|
||||
}),
|
||||
];
|
||||
|
||||
// Tasks handlers
|
||||
const tasksHandlers = [
|
||||
// Get all tasks
|
||||
http.get(`${API_URL}/api/tasks`, () => {
|
||||
return HttpResponse.json(mockTasks);
|
||||
}),
|
||||
|
||||
// Create a task
|
||||
http.post(`${API_URL}/api/tasks`, async ({ request }) => {
|
||||
const body = await request.json();
|
||||
const newTask = {
|
||||
_id: `task${Date.now()}`,
|
||||
...body,
|
||||
status: 'pending',
|
||||
createdBy: 'user123',
|
||||
};
|
||||
return HttpResponse.json(newTask);
|
||||
}),
|
||||
|
||||
// Complete a task
|
||||
http.put(`${API_URL}/api/tasks/:id/complete`, ({ params }) => {
|
||||
const task = mockTasks.find(t => t._id === params.id);
|
||||
|
||||
if (!task) {
|
||||
return HttpResponse.json(
|
||||
{ msg: 'Task not found' },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
return HttpResponse.json({
|
||||
...task,
|
||||
status: 'completed',
|
||||
});
|
||||
}),
|
||||
];
|
||||
|
||||
// Posts handlers
|
||||
const postsHandlers = [
|
||||
// Get all posts
|
||||
http.get(`${API_URL}/api/posts`, () => {
|
||||
return HttpResponse.json(mockPosts);
|
||||
}),
|
||||
|
||||
// Create a post
|
||||
http.post(`${API_URL}/api/posts`, async ({ request }) => {
|
||||
const body = await request.json();
|
||||
const newPost = {
|
||||
_id: `post${Date.now()}`,
|
||||
user: {
|
||||
_id: 'user123',
|
||||
name: 'Test User',
|
||||
profilePicture: null,
|
||||
},
|
||||
...body,
|
||||
likes: [],
|
||||
comments: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
return HttpResponse.json(newPost);
|
||||
}),
|
||||
|
||||
// Like a post
|
||||
http.put(`${API_URL}/api/posts/like/:id`, ({ params }) => {
|
||||
const post = mockPosts.find(p => p._id === params.id);
|
||||
|
||||
if (!post) {
|
||||
return HttpResponse.json(
|
||||
{ msg: 'Post not found' },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
return HttpResponse.json({
|
||||
...post,
|
||||
likes: [...post.likes, 'user123'],
|
||||
});
|
||||
}),
|
||||
];
|
||||
|
||||
// Events handlers
|
||||
const eventsHandlers = [
|
||||
// Get all events
|
||||
http.get(`${API_URL}/api/events`, () => {
|
||||
return HttpResponse.json(mockEvents);
|
||||
}),
|
||||
|
||||
// Create an event
|
||||
http.post(`${API_URL}/api/events`, async ({ request }) => {
|
||||
const body = await request.json();
|
||||
const newEvent = {
|
||||
_id: `event${Date.now()}`,
|
||||
...body,
|
||||
organizer: 'user123',
|
||||
participants: [],
|
||||
};
|
||||
return HttpResponse.json(newEvent);
|
||||
}),
|
||||
|
||||
// RSVP to an event
|
||||
http.put(`${API_URL}/api/events/rsvp/:id`, ({ params }) => {
|
||||
const event = mockEvents.find(e => e._id === params.id);
|
||||
|
||||
if (!event) {
|
||||
return HttpResponse.json(
|
||||
{ msg: 'Event not found' },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
return HttpResponse.json([...event.participants, 'user123']);
|
||||
}),
|
||||
];
|
||||
|
||||
// Rewards handlers
|
||||
const rewardsHandlers = [
|
||||
// Get all rewards
|
||||
http.get(`${API_URL}/api/rewards`, () => {
|
||||
return HttpResponse.json(mockRewards);
|
||||
}),
|
||||
|
||||
// Redeem a reward
|
||||
http.post(`${API_URL}/api/rewards/redeem/:id`, ({ params, request }) => {
|
||||
const reward = mockRewards.find(r => r._id === params.id);
|
||||
|
||||
if (!reward) {
|
||||
return HttpResponse.json(
|
||||
{ msg: 'Reward not found' },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
if (mockUser.points < reward.pointsCost) {
|
||||
return HttpResponse.json(
|
||||
{ msg: 'Not enough points' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (reward.isPremium && !mockUser.isPremium) {
|
||||
return HttpResponse.json(
|
||||
{ msg: 'Premium reward not available' },
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
return HttpResponse.json({ msg: 'Reward redeemed successfully' });
|
||||
}),
|
||||
];
|
||||
|
||||
// Users handlers
|
||||
const usersHandlers = [
|
||||
// Get user profile
|
||||
http.get(`${API_URL}/api/users/:id`, ({ params }) => {
|
||||
if (params.id === 'user123') {
|
||||
return HttpResponse.json(mockUser);
|
||||
}
|
||||
|
||||
return HttpResponse.json(
|
||||
{ msg: 'User not found' },
|
||||
{ status: 404 }
|
||||
);
|
||||
}),
|
||||
|
||||
// Update user profile
|
||||
http.put(`${API_URL}/api/users/:id`, async ({ params, request }) => {
|
||||
const body = await request.json();
|
||||
|
||||
if (params.id === 'user123') {
|
||||
return HttpResponse.json({
|
||||
...mockUser,
|
||||
...body,
|
||||
});
|
||||
}
|
||||
|
||||
return HttpResponse.json(
|
||||
{ msg: 'User not found' },
|
||||
{ status: 404 }
|
||||
);
|
||||
}),
|
||||
];
|
||||
|
||||
// Export all handlers
|
||||
export const handlers = [
|
||||
...authHandlers,
|
||||
...streetsHandlers,
|
||||
...tasksHandlers,
|
||||
...postsHandlers,
|
||||
...eventsHandlers,
|
||||
...rewardsHandlers,
|
||||
...usersHandlers,
|
||||
];
|
||||
@@ -0,0 +1,5 @@
|
||||
import { setupServer } from 'msw/node';
|
||||
import { handlers } from './handlers';
|
||||
|
||||
// Setup MSW server with default handlers
|
||||
export const server = setupServer(...handlers);
|
||||
Reference in New Issue
Block a user