Added AppHeader

This commit is contained in:
Eljakim Herrewijnen 2022-12-28 19:51:36 +01:00
parent 927b834445
commit 4ef64237de
24 changed files with 394 additions and 40 deletions

View File

@ -27,9 +27,7 @@ DEBUG = True
ALLOWED_HOSTS = [] ALLOWED_HOSTS = []
CORS_ORIGIN_WHITELIST = [
'http://localhost:3000',
]
# Application definition # Application definition
@ -40,11 +38,13 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'project',
'corsheaders', 'corsheaders',
'rest_framework', 'rest_framework',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
@ -104,6 +104,9 @@ AUTH_PASSWORD_VALIDATORS = [
}, },
] ]
CORS_ORIGIN_WHITELIST = [
'http://localhost:3000',
]
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/4.1/topics/i18n/ # https://docs.djangoproject.com/en/4.1/topics/i18n/

View File

@ -14,8 +14,14 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import path from django.urls import path, include
from rest_framework import routers
from project import views
router = routers.DefaultRouter()
router.register('projects', views.ProjectView, 'projects')
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('api/', include(router.urls))
] ]

View File

@ -0,0 +1,5 @@
import enum
class ProjectStatus(enum.Enum):
RUNNING = 1
ENDED = 2

View File

@ -0,0 +1,8 @@
from django.contrib import admin
from .models import Project
class ProjectAdmin(admin.ModelAdmin):
list = ('title', 'description', 'visible')
# Register projects
admin.site.register(Project, ProjectAdmin)

View File

@ -0,0 +1,28 @@
# Generated by Django 4.1.4 on 2022-12-28 15:11
from django.db import migrations, models
import herreweb_backend.utils
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Project',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=100)),
('description', models.TextField(default='No description added')),
('status', models.IntegerField(default=herreweb_backend.utils.ProjectStatus['RUNNING'])),
('image', models.ImageField(null=True, upload_to='')),
('rtd_url', models.CharField(max_length=200, null=True)),
('url', models.CharField(max_length=200, null=True)),
('visible', models.BooleanField(default=False)),
],
),
]

View File

@ -0,0 +1,14 @@
from django.db import models
from herreweb_backend.utils import *
class Project(models.Model):
title = models.CharField(max_length=100)
description = models.TextField(default="No description added")
status = models.IntegerField(default=ProjectStatus.RUNNING)
image = models.ImageField(null=True)
rtd_url = models.CharField(max_length=200, null=True)
url = models.CharField(max_length=200, null=True)
visible = models.BooleanField(default=False)
def _str_(self):
return self.title

View File

@ -0,0 +1,7 @@
from rest_framework import serializers
from .models import Project
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ('id' ,'title', 'description', 'status')

View File

@ -0,0 +1,8 @@
from django.shortcuts import render
from rest_framework import viewsets
from project.serializers import ProjectSerializer
from .models import Project
class ProjectView(viewsets.ModelViewSet):
serializer_class = ProjectSerializer
queryset = Project.objects.all()

17
herreweb_frontend/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/src/Projects.js"
}
]
}

View File

@ -14,6 +14,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"react-transition-group": "^4.4.5",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
} }
}, },
@ -6589,6 +6590,15 @@
"utila": "~0.4" "utila": "~0.4"
} }
}, },
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"node_modules/dom-serializer": { "node_modules/dom-serializer": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
@ -14249,6 +14259,21 @@
} }
} }
}, },
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
},
"peerDependencies": {
"react": ">=16.6.0",
"react-dom": ">=16.6.0"
}
},
"node_modules/read-cache": { "node_modules/read-cache": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@ -21690,6 +21715,15 @@
"utila": "~0.4" "utila": "~0.4"
} }
}, },
"dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"requires": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"dom-serializer": { "dom-serializer": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
@ -27049,6 +27083,17 @@
"workbox-webpack-plugin": "^6.4.1" "workbox-webpack-plugin": "^6.4.1"
} }
}, },
"react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"requires": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
}
},
"read-cache": { "read-cache": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",

View File

@ -9,6 +9,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"react-transition-group": "^4.4.5",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
"scripts": { "scripts": {

View File

@ -1,25 +0,0 @@
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;

View File

@ -1,8 +0,0 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@ -0,0 +1,15 @@
import logo from '../static/logo.svg';
import '../static/App.css';
import Projects from './Projects';
import AppHeader from './AppHeader';
function App() {
return (
<div className="App">
<AppHeader />
<Projects />
</div>
);
}
export default App;

View File

@ -0,0 +1,53 @@
/* eslint-disable jsx-a11y/accessible-emoji */
import React, { useState, useEffect } from "react";
import "../static/AppHeader.css";
import { CSSTransition } from "react-transition-group";
import logo from '../static/logo.svg';
export default function AppHeader() {
const [isNavVisible, setNavVisibility] = useState(false);
const [isSmallScreen, setIsSmallScreen] = useState(false);
useEffect(() => {
const mediaQuery = window.matchMedia("(max-width: 700px)");
mediaQuery.addListener(handleMediaQueryChange);
handleMediaQueryChange(mediaQuery);
return () => {
mediaQuery.removeListener(handleMediaQueryChange);
};
}, []);
const handleMediaQueryChange = mediaQuery => {
if (mediaQuery.matches) {
setIsSmallScreen(true);
} else {
setIsSmallScreen(false);
}
};
const toggleNav = () => {
setNavVisibility(!isNavVisible);
};
return (
<header className="Header">
<img src={logo} className="Logo" alt="logo" />
<CSSTransition
in={!isSmallScreen || isNavVisible}
timeout={350}
classNames="NavAnimation"
unmountOnExit
>
<nav className="Nav">
<a href="/">Projects</a>
<a href="/">Blogs</a>
<a href="/">About Us</a>
</nav>
</CSSTransition>
<button onClick={toggleNav} className="Burger">
🍔
</button>
</header>
);
}

View File

@ -0,0 +1,65 @@
import React, { Component } from "react"
const projectItems = [
{
id: 1,
title: "Thelendar",
description: "Stronghold Kingdoms",
completed: 1
},
{
id: 2,
title: "Ghidra Assistant",
description: "Reversing tools for Ghidra",
completed: 0
},
{
id: 3,
title: "Kerk Tallies",
description: "Creating Tally lights for Church",
completed: 0
},
];
class Projects extends Component {
constructor(props) {
super(props);
this.state = { projectItems }
};
async componentDidMount() {
this.setState({projectItems});
try {
const res = await fetch('http://localhost:8000/api/projects/');
const projectItems = await res.json();
this.setState({projectItems});
} catch (e) {
console.log(e);
}
}
render() {
return (
<main className="content">
<div className="row">
<div className="col-md-6 col-sm-10 mx-auto p-0">
<div className="card p-3">
<ul className="list-group list-group-flush">
{this.state.projectItems.map(item => (
<div>
<h1>{item.title}</h1>
<span>{item.description}</span>
</div>
))}
</ul>
</div>
</div>
</div>
</main>
)
}
}
export default Projects;

View File

@ -1,13 +1,15 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import './index.css'; import './static/index.css';
import App from './App'; import App from './components/App';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root')); const root = ReactDOM.createRoot(document.getElementById('root'));
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
{/* <Projects /> */}
</React.StrictMode> </React.StrictMode>
); );

View File

@ -0,0 +1,109 @@
.Header {
position: fixed;
top: 0; /* Stick it to the top */
max-height: 70px;
width: 100vw;
display: grid;
grid-template-areas: "logo nav";
/* Cosmetics */
background-color: #282c34;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
}
.Logo {
grid-area: logo;
height: 70px;
}
.Nav {
display: grid;
grid-area: nav;
grid-template-columns: repeat(4, auto);
align-items: center;
justify-items: center;
}
.Nav a {
color: #fff;
font-size: 20px;
font-weight: 500;
transition: 0.5s;
text-decoration: none;
}
.Nav a:hover {
transform: scale(1.1);
}
.Nav button {
padding: 10px;
outline: none;
border: none;
font-size: 20px;
color: #fff;
font-weight: 600;
background-color: rgba(255, 0, 0, 0.5);
box-shadow: 0px 5px 0px 0px rgba(255, 0, 0, 0.25);
border-radius: 10px;
cursor: pointer;
transition: 70ms;
}
.Nav button:active {
transform: translateY(3px);
box-shadow: 0px 2px 0px 0px rgba(255, 0, 0, 0.25);
}
.Burger {
display: none;
grid-area: burger;
margin: 0 20px 0 0;
padding: 0;
justify-self: end;
font-size: 40px;
border: none;
background: none;
outline: none;
transition: 0.1s;
}
.Burger:active {
transform: scale(1.2);
}
@media (max-width: 700px) {
.Header {
grid-template-areas: "logo burger" "nav nav";
}
.Nav {
grid-template-rows: repeat(4, auto);
grid-template-columns: none;
grid-row-gap: 20px;
padding: 30px 0 30px;
background: rgba(40, 44, 47, 0.95);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
}
.Burger {
display: inline;
}
}
.NavAnimation-enter {
opacity: 0;
transform: scale(0.5);
}
.NavAnimation-enter-active {
opacity: 1;
transform: translateX(0);
transition: opacity 350ms, transform 350ms;
}
.NavAnimation-exit {
opacity: 1;
}
.NavAnimation-exit-active {
opacity: 0;
transform: scale(0.5);
transition: opacity 350ms, transform 350ms;
}

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -2,3 +2,4 @@ django
sphinx sphinx
djangorestframework djangorestframework
django-cors-headers django-cors-headers
Pillow