Compare commits
4 Commits
345315db38
...
1.0.1
Author | SHA1 | Date | |
---|---|---|---|
4070b4e46e | |||
4892218c83 | |||
6b00a950ce | |||
feee9d6dac |
@ -25,7 +25,9 @@ steps:
|
|||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
settings:
|
settings:
|
||||||
repo: registry.kucharczyk.xyz/timetracker
|
repo: registry.kucharczyk.xyz/timetracker
|
||||||
auto_tag: true
|
tags:
|
||||||
|
- ${DRONE_COMMIT_REF}
|
||||||
|
- ${DRONE_COMMIT_BRANCH}
|
||||||
when:
|
when:
|
||||||
branch:
|
branch:
|
||||||
exclude:
|
exclude:
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
## Unrelease
|
## 1.0.1 / 2023-01-30 22:17+01:00
|
||||||
|
|
||||||
* Add React
|
* Make it possible to edit sessions (https://git.kucharczyk.xyz/lukas/timetracker/issues/46)
|
||||||
|
* Show markers on smaller graphs to make it clearer which dates the session belong to
|
||||||
|
* Show only last 30 days on the homepage (https://git.kucharczyk.xyz/lukas/timetracker/issues/47)
|
||||||
|
|
||||||
## 1.0.0 / 2023-01-20 19:54+01:00
|
## 1.0.0 / 2023-01-20 19:54+01:00
|
||||||
|
|
||||||
|
10
Makefile
10
Makefile
@ -1,12 +1,18 @@
|
|||||||
all: migrate
|
all: css migrate
|
||||||
|
|
||||||
initialize: npm migrate sethookdir loadplatforms
|
initialize: npm css migrate sethookdir loadplatforms
|
||||||
|
|
||||||
HTMLFILES := $(shell find games/templates -type f)
|
HTMLFILES := $(shell find games/templates -type f)
|
||||||
|
|
||||||
npm:
|
npm:
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
|
css: input.css
|
||||||
|
npx tailwindcss -i ./input.css -o ./games/static/base.css
|
||||||
|
|
||||||
|
css-dev: css
|
||||||
|
npx tailwindcss -i ./input.css -o ./games/static/base.css --watch
|
||||||
|
|
||||||
makemigrations:
|
makemigrations:
|
||||||
poetry run python manage.py makemigrations
|
poetry run python manage.py makemigrations
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ def get_chart(data, title="", xlabel="", ylabel=""):
|
|||||||
plt.switch_backend("SVG")
|
plt.switch_backend("SVG")
|
||||||
fig, ax = plt.subplots()
|
fig, ax = plt.subplots()
|
||||||
fig.set_size_inches(10, 4)
|
fig.set_size_inches(10, 4)
|
||||||
ax.plot(x, y)
|
lines = ax.plot(x, y, "-o")
|
||||||
first = x[0]
|
first = x[0]
|
||||||
last = x[-1]
|
last = x[-1]
|
||||||
difference = last - first
|
difference = last - first
|
||||||
@ -76,7 +76,11 @@ def get_chart(data, title="", xlabel="", ylabel=""):
|
|||||||
elif difference.days < 720:
|
elif difference.days < 720:
|
||||||
ax.xaxis.set_major_locator(mdates.MonthLocator())
|
ax.xaxis.set_major_locator(mdates.MonthLocator())
|
||||||
ax.xaxis.set_minor_locator(mdates.WeekdayLocator())
|
ax.xaxis.set_minor_locator(mdates.WeekdayLocator())
|
||||||
|
for line in lines:
|
||||||
|
line.set_marker("")
|
||||||
else:
|
else:
|
||||||
|
for line in lines:
|
||||||
|
line.set_marker("")
|
||||||
ax.xaxis.set_major_locator(mdates.YearLocator())
|
ax.xaxis.set_major_locator(mdates.YearLocator())
|
||||||
ax.xaxis.set_minor_locator(mdates.MonthLocator())
|
ax.xaxis.set_minor_locator(mdates.MonthLocator())
|
||||||
|
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
es2021: true
|
|
||||||
},
|
|
||||||
extends: ["eslint/recommended", "plugin:react/recommended", "plugin:prettier/recommended"],
|
|
||||||
overrides: [],
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: "latest",
|
|
||||||
sourceType: "module"
|
|
||||||
},
|
|
||||||
plugins: ["react"],
|
|
||||||
rules: {},
|
|
||||||
parserOptions: {
|
|
||||||
ecmaFeatures: { jsx: true }
|
|
||||||
}
|
|
||||||
};
|
|
24
frontend/.gitignore
vendored
24
frontend/.gitignore
vendored
@ -1,24 +0,0 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
node_modules
|
|
||||||
dist
|
|
||||||
dist-ssr
|
|
||||||
*.local
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"semi": true,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"printWidth": 100,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"bracketSameLine": false,
|
|
||||||
"singleAttributePerLine": true
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="description" content="Self-hosted time-tracker."/>
|
|
||||||
<meta name="keywords" content="time, tracking, video games, self-hosted"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<!-- TODO: replace with own icon -->
|
|
||||||
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
|
|
||||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css"/>
|
|
||||||
<title>Timetracker</title>
|
|
||||||
</head>
|
|
||||||
<body class="dark">
|
|
||||||
<div id="root"></div>
|
|
||||||
<script type="module" src="/src/main.jsx"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,33 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "frontend",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite",
|
|
||||||
"build": "vite build",
|
|
||||||
"preview": "vite preview"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"autoprefixer": "^10.4.13",
|
|
||||||
"eslint-config-prettier": "^8.6.0",
|
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
|
||||||
"postcss": "^8.4.21",
|
|
||||||
"prettier": "^2.8.3",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"react-router-dom": "^6.7.0",
|
|
||||||
"tailwindcss": "^3.2.4"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/react": "^18.0.26",
|
|
||||||
"@types/react-dom": "^18.0.9",
|
|
||||||
"@vitejs/plugin-react": "^3.0.0",
|
|
||||||
"eslint": "^8.32.0",
|
|
||||||
"eslint-plugin-import": "^2.27.5",
|
|
||||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
|
||||||
"eslint-plugin-react": "^7.32.1",
|
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
|
||||||
"vite": "^4.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {},
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
import "./App.css";
|
|
||||||
import Nav from "./components/Nav";
|
|
||||||
import { Outlet, useLoaderData } from "react-router-dom";
|
|
||||||
|
|
||||||
function App() {
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="dark:bg-gray-800 min-h-screen">
|
|
||||||
<Nav />
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
{/* {% load version %} */}
|
|
||||||
{/* <span className="fixed left-2 bottom-2 text-xs text-slate-300 dark:text-slate-600">{% version %} ({% version_date %})</span> */}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App;
|
|
@ -1,71 +0,0 @@
|
|||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
function Nav() {
|
|
||||||
return (
|
|
||||||
<nav className="mb-4 bg-white dark:bg-gray-900 border-gray-200 rounded">
|
|
||||||
<div className="container flex flex-wrap items-center justify-between mx-auto">
|
|
||||||
<Link
|
|
||||||
to="/"
|
|
||||||
className="flex items-center"
|
|
||||||
>
|
|
||||||
<span className="text-4xl">⌚</span>
|
|
||||||
<span className="self-center text-xl font-semibold whitespace-nowrap text-white">
|
|
||||||
Timetracker
|
|
||||||
</span>
|
|
||||||
</Link>
|
|
||||||
<div className="w-full md:block md:w-auto">
|
|
||||||
<ul className="flex flex-col md:flex-row p-4 mt-4 dark:text-white">
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
className="block py-2 pl-3 pr-4 hover:underline"
|
|
||||||
href="{% url 'add_game' %}"
|
|
||||||
>
|
|
||||||
New Game
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
className="block py-2 pl-3 pr-4 hover:underline"
|
|
||||||
href="{% url 'add_platform' %}"
|
|
||||||
>
|
|
||||||
New Platform
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{/* {% if game_available and platform_available %} */}
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
className="block py-2 pl-3 pr-4 hover:underline"
|
|
||||||
href="{% url 'add_purchase' %}"
|
|
||||||
>
|
|
||||||
New Purchase
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{/* {% endif %} */}
|
|
||||||
{/* {% if purchase_available %} */}
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
className="block py-2 pl-3 pr-4 hover:underline"
|
|
||||||
href="{% url 'add_session' %}"
|
|
||||||
>
|
|
||||||
New Session
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{/* {% endif %} */}
|
|
||||||
{/* {% if session_count > 0 %} */}
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className="block py-2 pl-3 pr-4 hover:underline"
|
|
||||||
to="/sessions"
|
|
||||||
>
|
|
||||||
All Sessions
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
{/* {% endif %} */}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Nav;
|
|
@ -1,162 +0,0 @@
|
|||||||
export default function SessionList() {
|
|
||||||
const data = [
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8000/api/sessions/25/",
|
|
||||||
"timestamp_start": "2020-01-01T00:00:00+01:00",
|
|
||||||
"timestamp_end": null,
|
|
||||||
"duration_manual": "12:00:00",
|
|
||||||
"duration_calculated": "00:00:00",
|
|
||||||
"note": "",
|
|
||||||
"purchase": "http://localhost:8000/api/purchases/3/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8000/api/sessions/26/",
|
|
||||||
"timestamp_start": "2022-12-31T15:25:00+01:00",
|
|
||||||
"timestamp_end": "2022-12-31T17:25:00+01:00",
|
|
||||||
"duration_manual": "00:00:00",
|
|
||||||
"duration_calculated": "02:00:00",
|
|
||||||
"note": "",
|
|
||||||
"purchase": "http://localhost:8000/api/purchases/2/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8000/api/sessions/27/",
|
|
||||||
"timestamp_start": "2023-01-01T23:00:00+01:00",
|
|
||||||
"timestamp_end": "2023-01-02T00:28:00+01:00",
|
|
||||||
"duration_manual": "00:00:00",
|
|
||||||
"duration_calculated": "01:28:00",
|
|
||||||
"note": "",
|
|
||||||
"purchase": "http://localhost:8000/api/purchases/3/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8000/api/sessions/28/",
|
|
||||||
"timestamp_start": "2023-01-02T22:08:00+01:00",
|
|
||||||
"timestamp_end": "2023-01-03T01:08:00+01:00",
|
|
||||||
"duration_manual": "00:00:00",
|
|
||||||
"duration_calculated": "03:00:00",
|
|
||||||
"note": "",
|
|
||||||
"purchase": "http://localhost:8000/api/purchases/3/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8000/api/sessions/29/",
|
|
||||||
"timestamp_start": "2023-01-03T22:36:00+01:00",
|
|
||||||
"timestamp_end": "2023-01-04T00:12:00+01:00",
|
|
||||||
"duration_manual": "00:00:00",
|
|
||||||
"duration_calculated": "01:36:00",
|
|
||||||
"note": "",
|
|
||||||
"purchase": "http://localhost:8000/api/purchases/3/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8000/api/sessions/30/",
|
|
||||||
"timestamp_start": "2023-01-04T20:35:00+01:00",
|
|
||||||
"timestamp_end": "2023-01-04T22:36:00+01:00",
|
|
||||||
"duration_manual": "00:00:00",
|
|
||||||
"duration_calculated": "02:01:00",
|
|
||||||
"note": "",
|
|
||||||
"purchase": "http://localhost:8000/api/purchases/3/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8000/api/sessions/31/",
|
|
||||||
"timestamp_start": "2023-01-06T18:48:00+01:00",
|
|
||||||
"timestamp_end": "2023-01-06T23:39:00+01:00",
|
|
||||||
"duration_manual": "00:00:00",
|
|
||||||
"duration_calculated": "04:51:00",
|
|
||||||
"note": "",
|
|
||||||
"purchase": "http://localhost:8000/api/purchases/3/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8000/api/sessions/32/",
|
|
||||||
"timestamp_start": "2023-01-07T23:49:00+01:00",
|
|
||||||
"timestamp_end": "2023-01-08T01:43:00+01:00",
|
|
||||||
"duration_manual": "00:00:00",
|
|
||||||
"duration_calculated": "01:54:00",
|
|
||||||
"note": "",
|
|
||||||
"purchase": "http://localhost:8000/api/purchases/3/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8000/api/sessions/33/",
|
|
||||||
"timestamp_start": "2023-01-08T16:21:00+01:00",
|
|
||||||
"timestamp_end": "2023-01-08T18:27:00+01:00",
|
|
||||||
"duration_manual": "00:00:00",
|
|
||||||
"duration_calculated": "02:06:00",
|
|
||||||
"note": "",
|
|
||||||
"purchase": "http://localhost:8000/api/purchases/3/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8000/api/sessions/34/",
|
|
||||||
"timestamp_start": "2023-01-08T19:04:00+01:00",
|
|
||||||
"timestamp_end": "2023-01-08T22:03:00+01:00",
|
|
||||||
"duration_manual": "00:00:00",
|
|
||||||
"duration_calculated": "02:59:00",
|
|
||||||
"note": "",
|
|
||||||
"purchase": "http://localhost:8000/api/purchases/3/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8000/api/sessions/35/",
|
|
||||||
"timestamp_start": "2023-01-09T19:35:48+01:00",
|
|
||||||
"timestamp_end": "2023-01-09T22:13:20.519058+01:00",
|
|
||||||
"duration_manual": "00:00:00",
|
|
||||||
"duration_calculated": "02:37:32.519058",
|
|
||||||
"note": "",
|
|
||||||
"purchase": "http://localhost:8000/api/purchases/3/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "http://localhost:8000/api/sessions/36/",
|
|
||||||
"timestamp_start": "2023-01-10T15:50:12+01:00",
|
|
||||||
"timestamp_end": "2023-01-10T17:03:45.424429+01:00",
|
|
||||||
"duration_manual": "00:00:00",
|
|
||||||
"duration_calculated": "01:13:33.424429",
|
|
||||||
"note": "",
|
|
||||||
"purchase": "http://localhost:8000/api/purchases/4/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const header = ["url", "timestamp_start", "timestamp_end", "duration_manual", "duration_calculated", "note", "purchase"]
|
|
||||||
// const header = ["Name", "Platform", "Start", "End", "Duration", "Manage"]
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div id="session-table" className="gap-4 shadow rounded-xl max-w-screen-lg mx-auto dark:bg-slate-700 p-2 justify-center">
|
|
||||||
{header.map(column => {
|
|
||||||
<div className="dark:border-white dark:text-slate-300 text-lg">{column}</div>
|
|
||||||
})}
|
|
||||||
{data.map(session => {
|
|
||||||
<>
|
|
||||||
<div className="dark:text-white overflow-hidden text-ellipsis whitespace-nowrap">
|
|
||||||
<a className="hover:underline" href="">
|
|
||||||
{ session.url }
|
|
||||||
</a>
|
|
||||||
<div className="dark:text-white overflow-hidden text-ellipsis whitespace-nowrap">
|
|
||||||
<a className="hover:underline" href="">
|
|
||||||
{ session.timestamp_start }
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="dark:text-white overflow-hidden text-ellipsis whitespace-nowrap">
|
|
||||||
<a className="hover:underline" href="">
|
|
||||||
{ session.timestamp_end }
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="dark:text-white overflow-hidden text-ellipsis whitespace-nowrap">
|
|
||||||
<a className="hover:underline" href="">
|
|
||||||
{ session.duration_manual }
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="dark:text-white overflow-hidden text-ellipsis whitespace-nowrap">
|
|
||||||
<a className="hover:underline" href="">
|
|
||||||
{ session.duration_calculated }
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="dark:text-white overflow-hidden text-ellipsis whitespace-nowrap">
|
|
||||||
<a className="hover:underline" href="">
|
|
||||||
{ session.note }
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="dark:text-white overflow-hidden text-ellipsis whitespace-nowrap">
|
|
||||||
<a className="hover:underline" href="">
|
|
||||||
{ session.purchase }
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
import { useRouteError } from "react-router-dom";
|
|
||||||
|
|
||||||
export default function ErrorPage() {
|
|
||||||
const error = useRouteError()
|
|
||||||
console.error(error)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="container text-center">
|
|
||||||
<h1 className="text-3xl">Oops!</h1>
|
|
||||||
<p>Sorry, an unexpected error has occurred.</p>
|
|
||||||
<p>
|
|
||||||
<i>{error.statusText || error.message}</i>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import ReactDOM from 'react-dom/client'
|
|
||||||
import App from './App'
|
|
||||||
import './index.css'
|
|
||||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
|
|
||||||
// import { loader as sessionLoader } from './routes/sessions'
|
|
||||||
import ErrorPage from "./error-page"
|
|
||||||
import SessionList from './components/SessionList'
|
|
||||||
// import Session from './routes/sessions'
|
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
|
||||||
{
|
|
||||||
path: "/",
|
|
||||||
element: <App />,
|
|
||||||
errorElement: <ErrorPage />,
|
|
||||||
// loader: sessionLoader,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: "sessions/",
|
|
||||||
element: <SessionList />
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// path: "sessions",
|
|
||||||
// element: <SessionList />
|
|
||||||
// }
|
|
||||||
])
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<RouterProvider router={router} />
|
|
||||||
</React.StrictMode>,
|
|
||||||
)
|
|
@ -1,17 +0,0 @@
|
|||||||
export async function api(url) {
|
|
||||||
const response = await fetch(url);
|
|
||||||
if (response.ok) {
|
|
||||||
const jsonValue = await response.json();
|
|
||||||
return Promise.resolve(jsonValue);
|
|
||||||
} else {
|
|
||||||
return Promise.reject('Response was not OK.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getSession(sessionId) {
|
|
||||||
return await api(`/api/sessions/${sessionId}/`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getSessionList() {
|
|
||||||
return await api(`/api/sessions/`);
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
module.exports = {
|
|
||||||
darkMode: "class",
|
|
||||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
|
||||||
theme: {
|
|
||||||
fontFamily: {
|
|
||||||
sans: ["Inter", "sans-serif"],
|
|
||||||
},
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
plugins: [require("@tailwindcss/typography"), require("@tailwindcss/forms")],
|
|
||||||
};
|
|
@ -1,15 +0,0 @@
|
|||||||
import { defineConfig } from 'vite';
|
|
||||||
import react from '@vitejs/plugin-react';
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [react()],
|
|
||||||
server: {
|
|
||||||
proxy: {
|
|
||||||
'/api': {
|
|
||||||
target: 'http://127.0.0.1:8000',
|
|
||||||
changeOrigin: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
@ -17,7 +17,7 @@
|
|||||||
<div class="dark:bg-gray-800 min-h-screen">
|
<div class="dark:bg-gray-800 min-h-screen">
|
||||||
<nav class="mb-4 bg-white dark:bg-gray-900 border-gray-200 rounded">
|
<nav class="mb-4 bg-white dark:bg-gray-900 border-gray-200 rounded">
|
||||||
<div class="container flex flex-wrap items-center justify-between mx-auto">
|
<div class="container flex flex-wrap items-center justify-between mx-auto">
|
||||||
<a href="{% url 'index' %}" class="flex items-center">
|
<a href="{% url 'list_sessions_recent' %}" class="flex items-center">
|
||||||
<span class="text-4xl">⌚</span>
|
<span class="text-4xl">⌚</span>
|
||||||
<span class="self-center text-xl font-semibold whitespace-nowrap text-white">Timetracker</span>
|
<span class="self-center text-xl font-semibold whitespace-nowrap text-white">Timetracker</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block title %}Sessions{% endblock title %}
|
{% block title %}{{ title }}{% endblock title %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="text-center text-xl mb-4 dark:text-slate-400">
|
<div class="text-center text-xl mb-4 dark:text-slate-400">
|
||||||
@ -62,12 +62,14 @@
|
|||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<a href="{% url 'edit_session' data.id %}">
|
||||||
<button type="button" title="Edit" class="py-1 px-2 flex justify-center items-center bg-blue-600 hover:bg-blue-700 focus:ring-blue-500 focus:ring-offset-blue-200 text-white transition ease-in duration-200 text-center text-base font-semibold shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 w-7 h-4 rounded-lg ">
|
<button type="button" title="Edit" class="py-1 px-2 flex justify-center items-center bg-blue-600 hover:bg-blue-700 focus:ring-blue-500 focus:ring-offset-blue-200 text-white transition ease-in duration-200 text-center text-base font-semibold shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 w-7 h-4 rounded-lg ">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
|
||||||
<path d="M5.433 13.917l1.262-3.155A4 4 0 017.58 9.42l6.92-6.918a2.121 2.121 0 013 3l-6.92 6.918c-.383.383-.84.685-1.343.886l-3.154 1.262a.5.5 0 01-.65-.65z" />
|
<path d="M5.433 13.917l1.262-3.155A4 4 0 017.58 9.42l6.92-6.918a2.121 2.121 0 013 3l-6.92 6.918c-.383.383-.84.685-1.343.886l-3.154 1.262a.5.5 0 01-.65-.65z" />
|
||||||
<path d="M3.5 5.75c0-.69.56-1.25 1.25-1.25H10A.75.75 0 0010 3H4.75A2.75 2.75 0 002 5.75v9.5A2.75 2.75 0 004.75 18h9.5A2.75 2.75 0 0017 15.25V10a.75.75 0 00-1.5 0v5.25c0 .69-.56 1.25-1.25 1.25h-9.5c-.69 0-1.25-.56-1.25-1.25v-9.5z" />
|
<path d="M3.5 5.75c0-.69.56-1.25 1.25-1.25H10A.75.75 0 0010 3H4.75A2.75 2.75 0 002 5.75v9.5A2.75 2.75 0 004.75 18h9.5A2.75 2.75 0 0017 15.25V10a.75.75 0 00-1.5 0v5.25c0 .69-.56 1.25-1.25 1.25h-9.5c-.69 0-1.25-.56-1.25-1.25v-9.5z" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
</a>
|
||||||
<a href="{% url 'delete_session' data.id %}">
|
<a href="{% url 'delete_session' data.id %}">
|
||||||
<button type="button" edit="Delete" class="py-1 px-2 flex justify-center items-center bg-red-600 hover:bg-red-700 focus:ring-red-500 focus:ring-offset-blue-200 text-white transition ease-in duration-200 text-center text-base font-semibold shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 w-7 h-4 rounded-lg ">
|
<button type="button" edit="Delete" class="py-1 px-2 flex justify-center items-center bg-red-600 hover:bg-red-700 focus:ring-red-500 focus:ring-offset-blue-200 text-white transition ease-in duration-200 text-center text-base font-semibold shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 w-7 h-4 rounded-lg ">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
|
||||||
|
@ -3,7 +3,7 @@ from django.urls import path
|
|||||||
from games import views
|
from games import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", views.index, name="index"),
|
path("", views.list_sessions, {"filter": "recent"}, name="list_sessions_recent"),
|
||||||
path("add-game/", views.add_game, name="add_game"),
|
path("add-game/", views.add_game, name="add_game"),
|
||||||
path("add-platform/", views.add_platform, name="add_platform"),
|
path("add-platform/", views.add_platform, name="add_platform"),
|
||||||
path("add-session/", views.add_session, name="add_session"),
|
path("add-session/", views.add_session, name="add_session"),
|
||||||
@ -23,6 +23,7 @@ urlpatterns = [
|
|||||||
name="delete_session",
|
name="delete_session",
|
||||||
),
|
),
|
||||||
path("add-purchase/", views.add_purchase, name="add_purchase"),
|
path("add-purchase/", views.add_purchase, name="add_purchase"),
|
||||||
|
path("edit-session/<int:session_id>", views.edit_session, name="edit_session"),
|
||||||
path("list-sessions/", views.list_sessions, name="list_sessions"),
|
path("list-sessions/", views.list_sessions, name="list_sessions"),
|
||||||
path(
|
path(
|
||||||
"list-sessions/by-purchase/<int:purchase_id>",
|
"list-sessions/by-purchase/<int:purchase_id>",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
from common.plots import playtime_over_time_chart
|
from common.plots import playtime_over_time_chart
|
||||||
@ -47,6 +47,18 @@ def update_session(request, session_id=None):
|
|||||||
return redirect("list_sessions")
|
return redirect("list_sessions")
|
||||||
|
|
||||||
|
|
||||||
|
def edit_session(request, session_id=None):
|
||||||
|
context = {}
|
||||||
|
session = Session.objects.get(id=session_id)
|
||||||
|
form = SessionForm(request.POST or None, instance=session)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
return redirect("list_sessions")
|
||||||
|
context["title"] = "Edit Session"
|
||||||
|
context["form"] = form
|
||||||
|
return render(request, "add.html", context)
|
||||||
|
|
||||||
|
|
||||||
def start_session(request, purchase_id=None):
|
def start_session(request, purchase_id=None):
|
||||||
session = SessionForm({"purchase": purchase_id, "timestamp_start": now_with_tz()})
|
session = SessionForm({"purchase": purchase_id, "timestamp_start": now_with_tz()})
|
||||||
session.save()
|
session.save()
|
||||||
@ -61,6 +73,7 @@ def delete_session(request, session_id=None):
|
|||||||
|
|
||||||
def list_sessions(request, filter="", purchase_id="", platform_id="", game_id=""):
|
def list_sessions(request, filter="", purchase_id="", platform_id="", game_id=""):
|
||||||
context = {}
|
context = {}
|
||||||
|
context["title"] = "Sessions"
|
||||||
|
|
||||||
if filter == "purchase":
|
if filter == "purchase":
|
||||||
dataset = Session.objects.filter(purchase=purchase_id)
|
dataset = Session.objects.filter(purchase=purchase_id)
|
||||||
@ -71,6 +84,11 @@ def list_sessions(request, filter="", purchase_id="", platform_id="", game_id=""
|
|||||||
elif filter == "game":
|
elif filter == "game":
|
||||||
dataset = Session.objects.filter(purchase__game=game_id)
|
dataset = Session.objects.filter(purchase__game=game_id)
|
||||||
context["game"] = Game.objects.get(id=game_id)
|
context["game"] = Game.objects.get(id=game_id)
|
||||||
|
elif filter == "recent":
|
||||||
|
dataset = Session.objects.filter(
|
||||||
|
timestamp_start__gte=datetime.now() - timedelta(days=30)
|
||||||
|
)
|
||||||
|
context["title"] = "Last 30 days"
|
||||||
else:
|
else:
|
||||||
# by default, sort from newest to oldest
|
# by default, sort from newest to oldest
|
||||||
dataset = Session.objects.all().order_by("-timestamp_start")
|
dataset = Session.objects.all().order_by("-timestamp_start")
|
||||||
@ -127,10 +145,3 @@ def add_platform(request):
|
|||||||
context["form"] = form
|
context["form"] = form
|
||||||
context["title"] = "Add New Platform"
|
context["title"] = "Add New Platform"
|
||||||
return render(request, "add.html", context)
|
return render(request, "add.html", context)
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
|
||||||
context = {}
|
|
||||||
context["total_duration"] = Session().duration_sum
|
|
||||||
context["title"] = "Index"
|
|
||||||
return render(request, "index.html", context)
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/forms": "^0.5.3",
|
"@tailwindcss/forms": "^0.5.3",
|
||||||
"@tailwindcss/typography": "^0.5.9"
|
"@tailwindcss/typography": "^0.5.8",
|
||||||
|
"tailwindcss": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
54
poetry.lock
generated
54
poetry.lock
generated
@ -220,21 +220,6 @@ tzdata = {version = "*", markers = "sys_platform == \"win32\""}
|
|||||||
argon2 = ["argon2-cffi (>=19.1.0)"]
|
argon2 = ["argon2-cffi (>=19.1.0)"]
|
||||||
bcrypt = ["bcrypt"]
|
bcrypt = ["bcrypt"]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "django-cors-headers"
|
|
||||||
version = "3.13.0"
|
|
||||||
description = "django-cors-headers is a Django application for handling the server headers required for Cross-Origin Resource Sharing (CORS)."
|
|
||||||
category = "main"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
files = [
|
|
||||||
{file = "django-cors-headers-3.13.0.tar.gz", hash = "sha256:f9dc6b4e3f611c3199700b3e5f3398c28757dcd559c2f82932687f3d0443cfdf"},
|
|
||||||
{file = "django_cors_headers-3.13.0-py3-none-any.whl", hash = "sha256:37e42883b5f1f2295df6b4bba96eb2417a14a03270cb24b2a07f021cd4487cf4"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
Django = ">=3.2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-extensions"
|
name = "django-extensions"
|
||||||
version = "3.2.1"
|
version = "3.2.1"
|
||||||
@ -250,36 +235,6 @@ files = [
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
Django = ">=3.2"
|
Django = ">=3.2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "django-rest-framework"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "alias."
|
|
||||||
category = "main"
|
|
||||||
optional = false
|
|
||||||
python-versions = "*"
|
|
||||||
files = [
|
|
||||||
{file = "django-rest-framework-0.1.0.tar.gz", hash = "sha256:47a8f496fa69e3b6bd79f68dd7a1527d907d6b77f009e9db7cf9bb21cc565e4a"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
djangorestframework = "*"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "djangorestframework"
|
|
||||||
version = "3.14.0"
|
|
||||||
description = "Web APIs for Django, made easy."
|
|
||||||
category = "main"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.6"
|
|
||||||
files = [
|
|
||||||
{file = "djangorestframework-3.14.0-py3-none-any.whl", hash = "sha256:eb63f58c9f218e1a7d064d17a70751f528ed4e1d35547fdade9aaf4cd103fd08"},
|
|
||||||
{file = "djangorestframework-3.14.0.tar.gz", hash = "sha256:579a333e6256b09489cbe0a067e66abe55c6595d8926be6b99423786334350c8"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
django = ">=3.0"
|
|
||||||
pytz = "*"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "djhtml"
|
name = "djhtml"
|
||||||
version = "1.5.2"
|
version = "1.5.2"
|
||||||
@ -873,13 +828,6 @@ files = [
|
|||||||
{file = "Pillow-9.4.0-1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858"},
|
{file = "Pillow-9.4.0-1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858"},
|
||||||
{file = "Pillow-9.4.0-1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab"},
|
{file = "Pillow-9.4.0-1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab"},
|
||||||
{file = "Pillow-9.4.0-1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9"},
|
{file = "Pillow-9.4.0-1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9"},
|
||||||
{file = "Pillow-9.4.0-2-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0"},
|
|
||||||
{file = "Pillow-9.4.0-2-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f"},
|
|
||||||
{file = "Pillow-9.4.0-2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c"},
|
|
||||||
{file = "Pillow-9.4.0-2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848"},
|
|
||||||
{file = "Pillow-9.4.0-2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1"},
|
|
||||||
{file = "Pillow-9.4.0-2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33"},
|
|
||||||
{file = "Pillow-9.4.0-2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9"},
|
|
||||||
{file = "Pillow-9.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157"},
|
{file = "Pillow-9.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157"},
|
||||||
{file = "Pillow-9.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47"},
|
{file = "Pillow-9.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47"},
|
||||||
{file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343"},
|
{file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343"},
|
||||||
@ -1349,4 +1297,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.10"
|
||||||
content-hash = "7a7ba3831802cf91b722b956817a0606b7b8f97724b1a23e5e581657fb34ea19"
|
content-hash = "bf02e951b8c14fbe6f2709b6df7f9664f99ad5fbf7195d8e7b18c8574d00e683"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "timetracker"
|
name = "timetracker"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
description = "A simple time tracker."
|
description = "A simple time tracker."
|
||||||
authors = ["Lukáš Kucharczyk <lukas@kucharczyk.xyz>"]
|
authors = ["Lukáš Kucharczyk <lukas@kucharczyk.xyz>"]
|
||||||
license = "GPL"
|
license = "GPL"
|
||||||
@ -14,8 +14,6 @@ gunicorn = "^20.1.0"
|
|||||||
uvicorn = "^0.20.0"
|
uvicorn = "^0.20.0"
|
||||||
pandas = "^1.5.2"
|
pandas = "^1.5.2"
|
||||||
matplotlib = "^3.6.3"
|
matplotlib = "^3.6.3"
|
||||||
django-rest-framework = "^0.1.0"
|
|
||||||
django-cors-headers = "^3.13.0"
|
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
black = "^22.12.0"
|
black = "^22.12.0"
|
||||||
|
14
tailwind.config.js
Normal file
14
tailwind.config.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module.exports = {
|
||||||
|
darkMode: 'class',
|
||||||
|
content: ["./**/*.{html,js}"],
|
||||||
|
theme: {
|
||||||
|
fontFamily: {
|
||||||
|
sans: ['Inter', 'sans-serif'],
|
||||||
|
},
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
require('@tailwindcss/typography'),
|
||||||
|
require('@tailwindcss/forms')
|
||||||
|
],
|
||||||
|
}
|
@ -12,7 +12,6 @@ https://docs.djangoproject.com/en/4.1/ref/settings/
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from corsheaders.defaults import default_headers, default_methods
|
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
@ -39,8 +38,6 @@ INSTALLED_APPS = [
|
|||||||
"django.contrib.sessions",
|
"django.contrib.sessions",
|
||||||
"django.contrib.messages",
|
"django.contrib.messages",
|
||||||
"django.contrib.staticfiles",
|
"django.contrib.staticfiles",
|
||||||
"rest_framework",
|
|
||||||
"corsheaders",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
@ -48,7 +45,6 @@ if DEBUG:
|
|||||||
INSTALLED_APPS.append("django.contrib.admin")
|
INSTALLED_APPS.append("django.contrib.admin")
|
||||||
|
|
||||||
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",
|
||||||
@ -153,24 +149,3 @@ if _csrf_trusted_origins:
|
|||||||
CSRF_TRUSTED_ORIGINS = _csrf_trusted_origins.split(",")
|
CSRF_TRUSTED_ORIGINS = _csrf_trusted_origins.split(",")
|
||||||
else:
|
else:
|
||||||
CSRF_TRUSTED_ORIGINS = []
|
CSRF_TRUSTED_ORIGINS = []
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
|
||||||
"DEFAULT_PERMISSION_CLASSES": [
|
|
||||||
"rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
FRONTEND_ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "frontend", "dist"))
|
|
||||||
CORS_ORIGIN_ALLOW_ALL = True
|
|
||||||
CORS_ALLOW_CREDENTIALS = True
|
|
||||||
|
|
||||||
CORS_ALLOW_HEADERS = list(default_headers) + [
|
|
||||||
"Accept-Language",
|
|
||||||
"Connection",
|
|
||||||
"Host",
|
|
||||||
"Origin",
|
|
||||||
"Referer",
|
|
||||||
"Sec-Fetch-Dest",
|
|
||||||
"Sec-Fetch-Mode",
|
|
||||||
"Sec-Fetch-Site",
|
|
||||||
]
|
|
||||||
|
@ -17,64 +17,10 @@ from django.conf import settings
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
from django.views.generic import RedirectView
|
from django.views.generic import RedirectView
|
||||||
from rest_framework import routers, serializers, viewsets
|
|
||||||
from games.models import Game, Purchase, Platform, Session
|
|
||||||
|
|
||||||
|
|
||||||
class GameSerializer(serializers.HyperlinkedModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Game
|
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
|
|
||||||
class PlatformSerializer(serializers.HyperlinkedModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Platform
|
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseSerializer(serializers.HyperlinkedModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Purchase
|
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
|
|
||||||
class SessionSerializer(serializers.HyperlinkedModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Session
|
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
|
|
||||||
class GameViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Game.objects.all()
|
|
||||||
serializer_class = GameSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class PlatformViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Platform.objects.all()
|
|
||||||
serializer_class = PlatformSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Purchase.objects.all()
|
|
||||||
serializer_class = PurchaseSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class SessionViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Session.objects.all()
|
|
||||||
serializer_class = SessionSerializer
|
|
||||||
|
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
|
||||||
router.register(r"games", GameViewSet)
|
|
||||||
router.register(r"platforms", PlatformViewSet)
|
|
||||||
router.register(r"purchases", PurchaseViewSet)
|
|
||||||
router.register(r"sessions", SessionViewSet)
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("api/", include(router.urls)),
|
path("", RedirectView.as_view(url="/tracker")),
|
||||||
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
|
path("tracker/", include("games.urls")),
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
|
Reference in New Issue
Block a user