Just started

This commit is contained in:
Chris Haulmark 2024-09-09 15:22:17 -06:00
commit aea0071b3b
16 changed files with 356 additions and 0 deletions

26
Dockerfile Normal file
View file

@ -0,0 +1,26 @@
# Build stage for client
FROM node:14 AS client-build
WORKDIR /app/client
COPY client/package*.json ./
RUN npm install
COPY client/ ./
RUN npm run build -- --mode production
# Build stage for server
FROM node:14 AS server-build
WORKDIR /app/server
COPY server/package*.json ./
RUN npm install
COPY server/ ./
RUN npm run build
# Production stage
FROM node:14-alpine
WORKDIR /app
COPY --from=client-build /app/client/build ./client/build
COPY --from=server-build /app/server/dist ./server/dist
COPY --from=server-build /app/server/package*.json ./server/
WORKDIR /app/server
RUN npm install --only=production
EXPOSE 5000
CMD ["node", "dist/index.js"]

28
README.md Normal file
View file

@ -0,0 +1,28 @@
# Missoula Council of the Deaf, Inc. Website
This repository contains the source code for the Missoula Council of the Deaf, Inc. (MCDi) website. The project is built using a MERN stack (MongoDB, Express, React, Node.js) with TypeScript and is containerized using Docker.
## Features
- Responsive design
- Animated UI components using Framer Motion
- Docker containerization for easy deployment
- Express.js backend serving static React files
## Project Structure
The project is organized into client and server directories:
- `client/`: React frontend application
- `server/`: Express.js backend application
- `Dockerfile`: Multi-stage build for both client and server
- `docker-compose.yml`: Docker Compose configuration for running the application
## Getting Started
To run this project locally:
1. Clone the repository
2. Install Docker and Docker Compose
3. Run `docker-compose up --build` in the project root directory
4. Access the website at `http://localhost:5000`

44
client/package.json Normal file
View file

@ -0,0 +1,44 @@
{
"name": "mcdi-website-client",
"version": "1.0.0",
"private": true,
"homepage": ".",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.10.0",
"styled-components": "^5.3.9",
"framer-motion": "^10.12.4"
},
"devDependencies": {
"@types/react": "^18.0.37",
"@types/react-dom": "^18.0.11",
"@types/styled-components": "^5.1.26",
"typescript": "^5.0.4",
"react-scripts": "5.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "GENERATE_SOURCEMAP=false react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Binary file not shown.

20
client/public/index.html Normal file
View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Missoula Council of the Deaf, Inc. Website"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Missoula Council of the Deaf, Inc.</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

23
client/src/App.tsx Normal file
View file

@ -0,0 +1,23 @@
import React from 'react';
import styled from 'styled-components';
import Header from './components/Header';
import Footer from './components/Footer';
import ComingSoon from './components/ComingSoon';
const AppContainer = styled.div`
min-height: 100vh;
position: relative;
padding-bottom: 60px; // Height of the footer
`;
const App: React.FC = () => {
return (
<AppContainer>
<Header />
<ComingSoon />
<Footer />
</AppContainer>
);
};
export default App;

View file

@ -0,0 +1,31 @@
import React from 'react';
import styled from 'styled-components';
import { motion } from 'framer-motion';
const ComingSoonContainer = styled.div`
display: flex;
justify-content: center;
align-items: center;
height: 60vh;
`;
const ComingSoonText = styled(motion.h2)`
font-size: 3rem;
color: #8C1D40;
`;
const ComingSoon: React.FC = () => {
return (
<ComingSoonContainer>
<ComingSoonText
initial={{ opacity: 0, y: -50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
>
COMING SOON
</ComingSoonText>
</ComingSoonContainer>
);
};
export default ComingSoon;

View file

@ -0,0 +1,22 @@
import React from 'react';
import styled from 'styled-components';
const FooterContainer = styled.footer`
background-color: #8C1D40;
color: white;
text-align: center;
padding: 1rem;
position: absolute;
bottom: 0;
width: 100%;
`;
const Footer: React.FC = () => {
return (
<FooterContainer>
<p>&copy; MCDi 2024</p>
</FooterContainer>
);
};
export default Footer;

View file

@ -0,0 +1,61 @@
import React from 'react';
import styled from 'styled-components';
import { motion } from 'framer-motion';
const HeaderContainer = styled.header`
background-color: #8C1D40;
color: white;
padding: 1rem;
`;
const Title = styled.h1`
font-size: 1.5rem;
text-align: right;
margin: 0;
`;
const Nav = styled.nav`
display: flex;
justify-content: center;
margin-top: 1rem;
`;
const NavItem = styled(motion.a)`
color: white;
text-decoration: none;
margin: 0 1rem;
position: relative;
&:after {
content: '';
position: absolute;
width: 100%;
height: 2px;
bottom: -5px;
left: 0;
background-color: white;
transform: scaleX(0);
transition: transform 0.3s ease-in-out;
}
&:hover:after {
transform: scaleX(1);
}
`;
const Header: React.FC = () => {
return (
<HeaderContainer>
<Title>Missoula Council of the Deaf, Inc</Title>
<Nav>
<NavItem href="/" whileHover={{ scale: 1.1 }}>Home</NavItem>
<NavItem href="/meet-board" whileHover={{ scale: 1.1 }}>Meet MCDi Board</NavItem>
<NavItem href="/MCD-Inc._AOI_Bylaws.pdf" whileHover={{ scale: 1.1 }}>Bylaws</NavItem>
<NavItem href="/news" whileHover={{ scale: 1.1 }}>News</NavItem>
<NavItem href="/calendar" whileHover={{ scale: 1.1 }}>Calendar</NavItem>
</Nav>
</HeaderContainer>
);
};
export default Header;

11
client/src/index.tsx Normal file
View file

@ -0,0 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './styles/main.css';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);

View file

@ -0,0 +1,14 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #F5F5F5;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

20
client/tsconfig.json Normal file
View file

@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"]
}

8
docker-compose.yml Normal file
View file

@ -0,0 +1,8 @@
version: '3'
services:
app:
build: .
ports:
- "5000:5000"
environment:
- NODE_ENV=production

20
server/package.json Normal file
View file

@ -0,0 +1,20 @@
{
"name": "mcdi-website-server",
"version": "1.0.0",
"main": "dist/index.js",
"scripts": {
"start": "node dist/index.js",
"build": "tsc",
"dev": "ts-node-dev --respawn --transpile-only src/index.ts"
},
"dependencies": {
"express": "^4.17.1",
"mongoose": "^6.0.12"
},
"devDependencies": {
"@types/express": "^4.17.13",
"@types/node": "^16.11.6",
"ts-node-dev": "^1.1.8",
"typescript": "^4.4.4"
}
}

17
server/src/index.ts Normal file
View file

@ -0,0 +1,17 @@
import express from 'express';
import path from 'path';
const app = express();
const PORT = process.env.PORT || 5000;
app.use(express.static(path.join(__dirname, '../../client/build')));
console.log('Serving static files from:', path.join(__dirname, '../../client/build'));
app.get('*', (req, res) => {
console.log('Serving index.html for path:', req.path);
res.sendFile(path.join(__dirname, '../../client/build/index.html'));
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});

11
server/tsconfig.json Normal file
View file

@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}