Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ ANTHROPIC_API_KEY=sk-ant...
EXAMPLES_PATH=~/development/examples
MCP_PATH=~/development/posthog/products/mcp
WIZARD_PATH=~/development/wizard
WIZARD_BIN=~/development/wizard/dist/bin.js
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have an update in the upcoming CI PR that'll fix this

Suggested change
WIZARD_BIN=~/development/wizard/dist/bin.js

2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ Desktop.ini
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.cursor/
**/.cursor/
.idea/
*.iml
*.sublime-*
Expand Down
4 changes: 4 additions & 0 deletions apps/react-router/react-router-v7-project/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.react-router
build
node_modules
README.md
8 changes: 8 additions & 0 deletions apps/react-router/react-router-v7-project/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
/node_modules/

# React Router
/.react-router/
/build/

.env
22 changes: 22 additions & 0 deletions apps/react-router/react-router-v7-project/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM node:20-alpine AS development-dependencies-env
COPY . /app
WORKDIR /app
RUN npm ci

FROM node:20-alpine AS production-dependencies-env
COPY ./package.json package-lock.json /app/
WORKDIR /app
RUN npm ci --omit=dev

FROM node:20-alpine AS build-env
COPY . /app/
COPY --from=development-dependencies-env /app/node_modules /app/node_modules
WORKDIR /app
RUN npm run build

FROM node:20-alpine
COPY ./package.json package-lock.json /app/
COPY --from=production-dependencies-env /app/node_modules /app/node_modules
COPY --from=build-env /app/build /app/build
WORKDIR /app
CMD ["npm", "run", "start"]
107 changes: 107 additions & 0 deletions apps/react-router/react-router-v7-project/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Mini REST Countries Explorer

A cool, interactive web application that lets you explore country data using the new **React Router V7** framework. This project fetches data from the [REST Countries API](https://restcountries.com) and allows you to filter and view detailed information about countries, including their names, capitals, regions, populations, and flags.

## Table of Contents

- [Features](#features)
- [Demo](#demo)
- [Installation](#installation)
- [Usage](#usage)
- [Project Structure](#project-structure)
- [Technologies Used](#technologies-used)
- [Contributing](#contributing)
- [License](#license)

## Features

- **Dynamic Routing:** Leverages React Router V7 to handle multiple routes and dynamic URL parameters.
- **Data Fetching:** Uses route loaders to retrieve country data from the REST Countries API.
- **Search & Filter:** Users can search for countries by name and filter by region.
- **Responsive UI:** Styled with Tailwind CSS to create a modern, responsive interface.
- **Modular Code:** Organized into reusable components for easy scaling and maintenance.

## Demo

_Link to Demo:_
[Live Demo](#)
_(Replace `#` with your live demo link when available)_

## Installation

1. **Clone the repository:**

```bash
git clone https://github.com/yourusername/mini-rest-countries-explorer.git
cd mini-rest-countries-explorer
```

2. **Install dependencies:**

```bash
npm install
```

3. **Run the development server:**

```bash
npm run dev
```

4. **Open your browser and navigate to:**

```
http://localhost:5173
```

## Usage

- **Home Page:** Get an introduction to the project with a hero section that explains the purpose of the explorer.
- **Countries Page:** View a list of all countries. Use the search input to filter by name and select a region from the dropdown to narrow down your results.
- **Country Detail Page:** Click on any country from the list to see detailed information, including a country flag, official name, capital, region, subregion, and population.
- **About Page:** Learn more about how the app uses the REST Countries API to display data and the technologies powering the project.

## Project Structure

```
mini-rest-countries-explorer/
├── index.html
├── package.json
├── tsconfig.json
├── vite.config.ts
└── src
├── main.tsx
└── app
├── root.tsx # Root layout with Header and Outlet
├── routes.ts # Routes configuration
├── routes
│ ├── home.tsx # Home page component
│ ├── countries.tsx # Countries list page with search & filtering
│ ├── country.tsx # Country detail page
│ └── about.tsx # About page explaining the project
└── components
└── Header.tsx # Shared header component with navigation
```

## Technologies Used

- **React:** JavaScript library for building user interfaces.
- **React Router V7:** For handling routing and navigation in our React application.
- **TypeScript:** Provides static typing for a more robust and maintainable codebase.
- **Tailwind CSS:** Utility-first CSS framework for styling.
- **Vite:** Fast development server and build tool.

## Contributing

Contributions are welcome! If you have any suggestions or improvements, please feel free to submit a pull request or open an issue.

1. Fork the repository.
2. Create a new branch (`git checkout -b feature/YourFeature`).
3. Make your changes.
4. Commit your changes (`git commit -m 'Add some feature'`).
5. Push to the branch (`git push origin feature/YourFeature`).
6. Open a pull request.

---

Happy coding and enjoy exploring the world with your Mini REST Countries Explorer!
3 changes: 3 additions & 0 deletions apps/react-router/react-router-v7-project/app/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { NavLink, Link } from "react-router";
import { useAuth } from "~/context/AuthContext";
import { getAvatarUrl } from "~/lib/utils/auth";

export default function Navbar() {
const { user, isAuthenticated } = useAuth();

return (
<header className="w-full px-8 text-gray-700 bg-white shadow-sm sticky top-0 z-50">
<div className="container flex flex-col md:flex-row items-center justify-between py-5 mx-auto max-w-7xl">
<div className="flex flex-col md:flex-row items-center">
<NavLink to="/" className="flex items-center mb-5 md:mb-0">
<span className="text-xl font-black text-gray-900 select-none">
🌍 Country<span className="text-indigo-600">Explorer</span>
</span>
</NavLink>
<nav className="flex flex-wrap items-center ml-0 md:ml-8 md:border-l md:pl-8">
<NavLink
to="/"
end
className="mr-5 font-medium text-gray-600 hover:text-gray-900"
>
Home
</NavLink>
<NavLink
to="/countries"
className="mr-5 font-medium text-gray-600 hover:text-gray-900"
>
Countries
</NavLink>
{isAuthenticated && (
<>
<NavLink
to="/profile"
className="mr-5 font-medium text-gray-600 hover:text-gray-900"
>
Profile
</NavLink>
<NavLink
to="/stats"
className="mr-5 font-medium text-gray-600 hover:text-gray-900"
>
Stats
</NavLink>
</>
)}
<NavLink
to="/about"
className="mr-5 font-medium text-gray-600 hover:text-gray-900"
>
About
</NavLink>
</nav>
</div>
<div className="flex items-center gap-4">
{isAuthenticated && user ? (
<Link
to="/profile"
className="flex items-center gap-2 px-4 py-2 bg-indigo-50 rounded-lg hover:bg-indigo-100 transition"
>
<img
src={getAvatarUrl(user.username)}
alt={user.username}
className="w-8 h-8 rounded-full"
/>
<span className="font-medium text-gray-700">{user.username}</span>
<span className="text-yellow-500">⭐ {user.totalPoints}</span>
</Link>
) : (
<div className="flex gap-3">
<Link
to="/login"
className="px-4 py-2 text-gray-600 hover:text-gray-900 font-medium"
>
Login
</Link>
<Link
to="/signup"
className="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 font-medium transition"
>
Sign Up
</Link>
</div>
)}
</div>
</div>
</header>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { createContext, useContext, useState, useEffect, type ReactNode } from 'react'
import { usePostHog } from '@posthog/react'
import type { FakeUser } from '~/lib/utils/auth'
import { getCurrentUser, setCurrentUser, fakeLogin, fakeSignup, fakeLogout } from '~/lib/utils/auth'

interface AuthContextType {
user: FakeUser | null
login: (username: string, password: string) => boolean
signup: (username: string, email: string, password: string) => FakeUser | null
logout: () => void
isAuthenticated: boolean
}

const AuthContext = createContext<AuthContextType | undefined>(undefined)

export function AuthProvider({ children }: { children: ReactNode }) {
const posthog = usePostHog()
const [user, setUser] = useState<FakeUser | null>(null)

useEffect(() => {
const currentUser = getCurrentUser()
setUser(currentUser)
}, [])

const login = (username: string, password: string): boolean => {
const loggedInUser = fakeLogin(username, password)
if (loggedInUser) {
setUser(loggedInUser)
return true
}
return false
}

const signup = (username: string, email: string, password: string): FakeUser | null => {
try {
const newUser = fakeSignup(username, email, password)
setUser(newUser)
return newUser
} catch (error) {
console.error('Signup error:', error)
return null
}
}

const logout = () => {
// Capture logout event before resetting
posthog?.capture('user_logged_out')
// Reset PostHog state to unlink future events from this user
posthog?.reset()

fakeLogout()
setUser(null)
}

// Sync user state when localStorage changes
useEffect(() => {
const handleStorageChange = () => {
const currentUser = getCurrentUser()
setUser(currentUser)
}
window.addEventListener('storage', handleStorageChange)
const interval = setInterval(() => {
const currentUser = getCurrentUser()
if (currentUser?.id !== user?.id) {
setUser(currentUser)
}
}, 1000)

return () => {
window.removeEventListener('storage', handleStorageChange)
clearInterval(interval)
}
}, [user?.id])

return (
<AuthContext.Provider
value={{
user,
login,
signup,
logout,
isAuthenticated: !!user,
}}
>
{children}
</AuthContext.Provider>
)
}

export function useAuth() {
const context = useContext(AuthContext)
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider')
}
return context
}

23 changes: 23 additions & 0 deletions apps/react-router/react-router-v7-project/app/entry.client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import { HydratedRouter } from "react-router/dom";

import posthog from 'posthog-js';
import { PostHogProvider } from '@posthog/react';

posthog.init(import.meta.env.VITE_PUBLIC_POSTHOG_KEY, {
api_host: import.meta.env.VITE_PUBLIC_POSTHOG_HOST,
defaults: '2025-05-24',
__add_tracing_headers: [window.location.host, 'localhost'],
});

startTransition(() => {
hydrateRoot(
document,
<PostHogProvider client={posthog}>
<StrictMode>
<HydratedRouter />
</StrictMode>
</PostHogProvider>,
);
});
Loading
Loading