Neptune Beer Club
A 4-day challenge for the web agency Lumy. I developed a web application to find bars in Brest 🍺, France. The criteria are based, among other things, on deployment, filtering system, and originality.
TYPE | Challenge, Web |
DATE | |
STACK | Vite, React, TypeScript, Tailwind CSS, React Map GL |
The problem
- See a map of Brest with all bars
- See the list of bars
- View details
- Filter bars
The solution
- Single Page Application (SPA) with the React, Vite and Tailwind library.
- Configuration: Vite, Vitest, Prettier, EsLint, TypeScript
- File organization: Screaming architecture with a features/bar folder
- Deployment: GitHub Actions
Screaming architecture
I tried to implement these features:
- index.js as a public API
- camel-case
- features & colocation
└── src/
├── assets/
├── features/
│ ├── bars/
│ │ ├── index.js (public API)
│ │ ├── api/index.ts
│ │ ├── bar-map/
│ │ ├── bar-form/
│ │ └── bar-list/
│ │ ├── index.js (public API)
│ │ ├── bar-item.component.js
│ │ ├── bar-list.component.js
│ │ ├── bar-list.context.js
│ │ ├── bar-list.test.js
│ │ └── use-bar-list.js
│ ├── users/ (new features, not yet)
│ │ ├── index.js
│ │ ├── login/
│ │ ├── signup/
│ │ └── use-auth.js
│ ├── ui/ (common, shared components)
│ ├── index.js
│ ├── components/
│ ├── containers/ : header, footer, navbar, ...
│ └── layouts/
└── pages/
└── home.tsx
Public API (index.js)
export * from './bar-list';
export * from './bar-map';
export * from './bar-filters';
export * from './bar-context';
Home.tsx
- The
<BarProvider />
is used to share data retrieved from the API - The
<MapProvider />
is provided by React Map GL to allow actions on the map from other components. You can click a button inthat sends a new position of the .
import { Header } from '@containers/Header';
import { BarList, BarMap, BarFilters, BarProvider } from '@features/bar';
import { MapProvider } from 'react-map-gl';
export const Home = () => {
return (
<BarProvider>
<MapProvider>
<div className="grid grid-cols-[1fr_2fr]">
<div className="flex h-screen flex-col">
<Header />
<BarFilters />
<BarList />
</div>
<div>
<BarMap />
</div>
</div>
</MapProvider>
</BarProvider>
);
};
React Map GL
<Map
id="barmap"
initialViewState={{
longitude: -4.4852,
latitude: 48.3891,
zoom: 14,
}}
mapStyle="mapbox://styles/mapbox/dark-v9"
mapboxAccessToken={MAPBOX_TOKEN}
interactiveLayerIds={[clusterLayer.id as string]}
ref={mapRef}
onClick={onClick}
>
{geojsonData ? (
<Source
id="brest-bars"
type="geojson"
data={geojsonData}
cluster={true}
clusterMaxZoom={14}
clusterRadius={50}
>
<Layer {...clusterLayer} />
<Layer {...clusterCountLayer} />
<Layer {...unclusteredPointLayer} />
</Source>
) : null}
<BarMapMarkers bars={filteredBars} />
</Map>
What I Learned
- Deployment with 2 GitHub Actions: testing and deployment
- Feature-based architecture with public API
- Using the map in React
- Overcome the difficulties of using the GeoJson format
Future Plans
The goal is to develop the “Neptune Beer Club” application, giving a charismatic and legendary identity to bars in Brest.
- Enhanced User Interaction: Implementing a search feature, providing directions, and enabling location sharing for a more interactive experience.
- Technical Optimization: Utilizing API Directus for efficient data management, deploying with Docker, and incorporating animations for improved technical aspects.
- Creative Branding and Promotion: Establishing a unique brand identity, promoting local bars and beers, and adding thematic elements for a creative touch.
- Innovative Features: Exploring the integration of an AI bot, experimenting with 3D effects, and providing inclusivity information for a modern and innovative application.
- Diverse User Scenarios: Offering users a choice between traditional (Couple, Brewer/Connoisseur, Friends, Business) and original scenarios involving a dog, a robot, or envisioning the future.