First version
This commit is contained in:
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
frontend/node_modules
|
10
.env.example
Normal file
10
.env.example
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
SECRET_KEY=''
|
||||||
|
DB_NAME=''
|
||||||
|
DB_USER=''
|
||||||
|
DB_PASSWORD=''
|
||||||
|
EMAIL_BACKEND="django.core.mail.backends.console.EmailBackend"
|
||||||
|
EMAIL_USE_TLS=...
|
||||||
|
EMAIL_HOST=''
|
||||||
|
EMAIL_HOST_USER=''
|
||||||
|
EMAIL_HOST_PASSWORD=''
|
||||||
|
EMAIL_PORT=...
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.env
|
16
Caddyfile
Normal file
16
Caddyfile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
:8080 {
|
||||||
|
encode zstd gzip
|
||||||
|
|
||||||
|
handle_path /static/* {
|
||||||
|
file_server {
|
||||||
|
root /static
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# handle_path /api/* {
|
||||||
|
# reverse_proxy backend:8000
|
||||||
|
# }
|
||||||
|
|
||||||
|
reverse_proxy /admin/* backend:8000
|
||||||
|
reverse_proxy frontend:3000
|
||||||
|
}
|
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Jak uruchomić
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.yaml.prod up -d --build
|
||||||
|
docker compose exec -it backend python mange.py migrate
|
||||||
|
docker compose exec -it backend python mange.py createsuperuser
|
||||||
|
```
|
||||||
|
|
||||||
|
Po czym
|
||||||
|
- backend jest dostępny na porcie ```8000```
|
||||||
|
- frontend jest dostępny na porcie ```3000```
|
||||||
|
|
||||||
|
## Produkcja
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.yaml.prod up -d --build
|
||||||
|
docker compose -f docker-compose.yaml.prod exec -it backend python mange.py migrate
|
||||||
|
docker compose -f docker-compose.yaml.prod exec -it backend python mange.py createsuperuser
|
||||||
|
docker compose -f docker-compose.yaml.prod exec -it backend python manage.py collectstatic
|
||||||
|
```
|
||||||
|
|
||||||
|
Po czym aplikacja jest dostępna na porcie ```8080```
|
29
backend/Dockerfile
Normal file
29
backend/Dockerfile
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE 1
|
||||||
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
python3-setuptools \
|
||||||
|
python3-pip \
|
||||||
|
python3-dev \
|
||||||
|
python3-venv \
|
||||||
|
netcat-openbsd && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN pip install --upgrade pip
|
||||||
|
COPY ./requirements.txt .
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
|
COPY ./entrypoint.sh .
|
||||||
|
RUN sed -i 's/\r$//g' /usr/src/app/entrypoint.sh
|
||||||
|
RUN chmod +x /usr/src/app/entrypoint.sh
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
|
23
backend/Pipfile
Normal file
23
backend/Pipfile
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
django = "*"
|
||||||
|
psycopg2-binary = "*"
|
||||||
|
django-browser-reload = "*"
|
||||||
|
six = "*"
|
||||||
|
channels-redis = "*"
|
||||||
|
redis = "*"
|
||||||
|
django-redis = "*"
|
||||||
|
channels = {extras = ["daphne"], version = "*"}
|
||||||
|
twisted = {extras = ["tls", "http2"], version = "*"}
|
||||||
|
djangorestframework = "*"
|
||||||
|
markdown = "*"
|
||||||
|
django-filter = "*"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.12"
|
587
backend/Pipfile.lock
generated
Normal file
587
backend/Pipfile.lock
generated
Normal file
@ -0,0 +1,587 @@
|
|||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "e6a67e78f8300789081bdb71bcddebc4d6823353c42327f8b5267670e4df5b51"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {
|
||||||
|
"python_version": "3.12"
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"asgiref": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47",
|
||||||
|
"sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==3.8.1"
|
||||||
|
},
|
||||||
|
"attrs": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346",
|
||||||
|
"sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==24.2.0"
|
||||||
|
},
|
||||||
|
"autobahn": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:a2d71ef1b0cf780b6d11f8b205fd2c7749765e65795f2ea7d823796642ee92c9",
|
||||||
|
"sha256:c56a2abe7ac78abbfb778c02892d673a4de58fd004d088cd7ab297db25918e81"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==24.4.2"
|
||||||
|
},
|
||||||
|
"automat": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:b34227cf63f6325b8ad2399ede780675083e439b20c323d376373d8ee6306d88",
|
||||||
|
"sha256:bf029a7bc3da1e2c24da2343e7598affaa9f10bf0ab63ff808566ce90551e02a"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==24.8.1"
|
||||||
|
},
|
||||||
|
"cffi": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8",
|
||||||
|
"sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2",
|
||||||
|
"sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1",
|
||||||
|
"sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15",
|
||||||
|
"sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36",
|
||||||
|
"sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824",
|
||||||
|
"sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8",
|
||||||
|
"sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36",
|
||||||
|
"sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17",
|
||||||
|
"sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf",
|
||||||
|
"sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc",
|
||||||
|
"sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3",
|
||||||
|
"sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed",
|
||||||
|
"sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702",
|
||||||
|
"sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1",
|
||||||
|
"sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8",
|
||||||
|
"sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903",
|
||||||
|
"sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6",
|
||||||
|
"sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d",
|
||||||
|
"sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b",
|
||||||
|
"sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e",
|
||||||
|
"sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be",
|
||||||
|
"sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c",
|
||||||
|
"sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683",
|
||||||
|
"sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9",
|
||||||
|
"sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c",
|
||||||
|
"sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8",
|
||||||
|
"sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1",
|
||||||
|
"sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4",
|
||||||
|
"sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655",
|
||||||
|
"sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67",
|
||||||
|
"sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595",
|
||||||
|
"sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0",
|
||||||
|
"sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65",
|
||||||
|
"sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41",
|
||||||
|
"sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6",
|
||||||
|
"sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401",
|
||||||
|
"sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6",
|
||||||
|
"sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3",
|
||||||
|
"sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16",
|
||||||
|
"sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93",
|
||||||
|
"sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e",
|
||||||
|
"sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4",
|
||||||
|
"sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964",
|
||||||
|
"sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c",
|
||||||
|
"sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576",
|
||||||
|
"sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0",
|
||||||
|
"sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3",
|
||||||
|
"sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662",
|
||||||
|
"sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3",
|
||||||
|
"sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff",
|
||||||
|
"sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5",
|
||||||
|
"sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd",
|
||||||
|
"sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f",
|
||||||
|
"sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5",
|
||||||
|
"sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14",
|
||||||
|
"sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d",
|
||||||
|
"sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9",
|
||||||
|
"sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7",
|
||||||
|
"sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382",
|
||||||
|
"sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a",
|
||||||
|
"sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e",
|
||||||
|
"sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a",
|
||||||
|
"sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4",
|
||||||
|
"sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99",
|
||||||
|
"sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87",
|
||||||
|
"sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"
|
||||||
|
],
|
||||||
|
"markers": "platform_python_implementation != 'PyPy'",
|
||||||
|
"version": "==1.17.1"
|
||||||
|
},
|
||||||
|
"channels": {
|
||||||
|
"extras": [
|
||||||
|
"daphne"
|
||||||
|
],
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6b75bc8d6888fb7236e7e7bf1948520b72d296ad08216a242fc56b1db0ffde1a",
|
||||||
|
"sha256:d9e707487431ba5dbce9af982970dab3b0efd786580fadb99e45dca5e39fdd59"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==4.2.0"
|
||||||
|
},
|
||||||
|
"channels-redis": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2ca33105b3a04b5a327a9c47dd762b546f30b76a0cd3f3f593a23d91d346b6f4",
|
||||||
|
"sha256:8375e81493e684792efe6e6eca60ef3d7782ef76c6664057d2e5c31e80d636dd"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==4.2.1"
|
||||||
|
},
|
||||||
|
"constantly": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3fd9b4d1c3dc1ec9757f3c52aef7e53ad9323dbe39f51dfd4c43853b68dfa3f9",
|
||||||
|
"sha256:aa92b70a33e2ac0bb33cd745eb61776594dc48764b06c35e0efd050b7f1c7cbd"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==23.10.4"
|
||||||
|
},
|
||||||
|
"cryptography": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7",
|
||||||
|
"sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731",
|
||||||
|
"sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b",
|
||||||
|
"sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc",
|
||||||
|
"sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543",
|
||||||
|
"sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c",
|
||||||
|
"sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591",
|
||||||
|
"sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede",
|
||||||
|
"sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb",
|
||||||
|
"sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f",
|
||||||
|
"sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123",
|
||||||
|
"sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c",
|
||||||
|
"sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c",
|
||||||
|
"sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285",
|
||||||
|
"sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd",
|
||||||
|
"sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092",
|
||||||
|
"sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa",
|
||||||
|
"sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289",
|
||||||
|
"sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02",
|
||||||
|
"sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64",
|
||||||
|
"sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053",
|
||||||
|
"sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417",
|
||||||
|
"sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e",
|
||||||
|
"sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e",
|
||||||
|
"sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7",
|
||||||
|
"sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756",
|
||||||
|
"sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'",
|
||||||
|
"version": "==44.0.0"
|
||||||
|
},
|
||||||
|
"daphne": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:618d1322bb4d875342b99dd2a10da2d9aae7ee3645f765965fdc1e658ea5290a",
|
||||||
|
"sha256:fcbcace38eb86624ae247c7ffdc8ac12f155d7d19eafac4247381896d6f33761"
|
||||||
|
],
|
||||||
|
"version": "==4.1.2"
|
||||||
|
},
|
||||||
|
"django": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:236e023f021f5ce7dee5779de7b286565fdea5f4ab86bae5338e3f7b69896cf0",
|
||||||
|
"sha256:de450c09e91879fa5a307f696e57c851955c910a438a35e6b4c895e86bedc82a"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.10'",
|
||||||
|
"version": "==5.1.4"
|
||||||
|
},
|
||||||
|
"django-browser-reload": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3667939cde0eee1a6d698dbe3b78cf10b573dabc4e711fb7933f1ba91fb98da4",
|
||||||
|
"sha256:d372c12c1c5962c02279a53cac7e8a020c48f104592c637a06d0768b28d2d6be"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==1.17.0"
|
||||||
|
},
|
||||||
|
"django-filter": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:c4852822928ce17fb699bcfccd644b3574f1a2d80aeb2b4ff4f16b02dd49dc64",
|
||||||
|
"sha256:d8ccaf6732afd21ca0542f6733b11591030fa98669f8d15599b358e24a2cd9c3"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==24.3"
|
||||||
|
},
|
||||||
|
"django-redis": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6a02abaa34b0fea8bf9b707d2c363ab6adc7409950b2db93602e6cb292818c42",
|
||||||
|
"sha256:ebc88df7da810732e2af9987f7f426c96204bf89319df4c6da6ca9a2942edd5b"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==5.4.0"
|
||||||
|
},
|
||||||
|
"djangorestframework": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20",
|
||||||
|
"sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==3.15.2"
|
||||||
|
},
|
||||||
|
"h2": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d",
|
||||||
|
"sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"
|
||||||
|
],
|
||||||
|
"version": "==4.1.0"
|
||||||
|
},
|
||||||
|
"hpack": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c",
|
||||||
|
"sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"
|
||||||
|
],
|
||||||
|
"markers": "python_full_version >= '3.6.1'",
|
||||||
|
"version": "==4.0.0"
|
||||||
|
},
|
||||||
|
"hyperframe": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15",
|
||||||
|
"sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"
|
||||||
|
],
|
||||||
|
"markers": "python_full_version >= '3.6.1'",
|
||||||
|
"version": "==6.0.1"
|
||||||
|
},
|
||||||
|
"hyperlink": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b",
|
||||||
|
"sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4"
|
||||||
|
],
|
||||||
|
"version": "==21.0.0"
|
||||||
|
},
|
||||||
|
"idna": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9",
|
||||||
|
"sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
|
||||||
|
],
|
||||||
|
"version": "==3.10"
|
||||||
|
},
|
||||||
|
"incremental": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:8cb2c3431530bec48ad70513931a760f446ad6c25e8333ca5d95e24b0ed7b8fe",
|
||||||
|
"sha256:fb4f1d47ee60efe87d4f6f0ebb5f70b9760db2b2574c59c8e8912be4ebd464c9"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==24.7.2"
|
||||||
|
},
|
||||||
|
"markdown": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2",
|
||||||
|
"sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==3.7"
|
||||||
|
},
|
||||||
|
"msgpack": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b",
|
||||||
|
"sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf",
|
||||||
|
"sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca",
|
||||||
|
"sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330",
|
||||||
|
"sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f",
|
||||||
|
"sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f",
|
||||||
|
"sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39",
|
||||||
|
"sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247",
|
||||||
|
"sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b",
|
||||||
|
"sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c",
|
||||||
|
"sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7",
|
||||||
|
"sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044",
|
||||||
|
"sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6",
|
||||||
|
"sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b",
|
||||||
|
"sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0",
|
||||||
|
"sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2",
|
||||||
|
"sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468",
|
||||||
|
"sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7",
|
||||||
|
"sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734",
|
||||||
|
"sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434",
|
||||||
|
"sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325",
|
||||||
|
"sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1",
|
||||||
|
"sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846",
|
||||||
|
"sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88",
|
||||||
|
"sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420",
|
||||||
|
"sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e",
|
||||||
|
"sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2",
|
||||||
|
"sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59",
|
||||||
|
"sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb",
|
||||||
|
"sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68",
|
||||||
|
"sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915",
|
||||||
|
"sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f",
|
||||||
|
"sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701",
|
||||||
|
"sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b",
|
||||||
|
"sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d",
|
||||||
|
"sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa",
|
||||||
|
"sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d",
|
||||||
|
"sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd",
|
||||||
|
"sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc",
|
||||||
|
"sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48",
|
||||||
|
"sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb",
|
||||||
|
"sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74",
|
||||||
|
"sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b",
|
||||||
|
"sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346",
|
||||||
|
"sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e",
|
||||||
|
"sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6",
|
||||||
|
"sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5",
|
||||||
|
"sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f",
|
||||||
|
"sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5",
|
||||||
|
"sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b",
|
||||||
|
"sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c",
|
||||||
|
"sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f",
|
||||||
|
"sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec",
|
||||||
|
"sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8",
|
||||||
|
"sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5",
|
||||||
|
"sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d",
|
||||||
|
"sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e",
|
||||||
|
"sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e",
|
||||||
|
"sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870",
|
||||||
|
"sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f",
|
||||||
|
"sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96",
|
||||||
|
"sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c",
|
||||||
|
"sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd",
|
||||||
|
"sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==1.1.0"
|
||||||
|
},
|
||||||
|
"priority": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6bc1961a6d7fcacbfc337769f1a382c8e746566aaa365e78047abe9f66b2ffbe",
|
||||||
|
"sha256:be4fcb94b5e37cdeb40af5533afe6dd603bd665fe9c8b3052610fc1001d5d1eb"
|
||||||
|
],
|
||||||
|
"version": "==1.3.0"
|
||||||
|
},
|
||||||
|
"psycopg2-binary": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff",
|
||||||
|
"sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5",
|
||||||
|
"sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f",
|
||||||
|
"sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5",
|
||||||
|
"sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0",
|
||||||
|
"sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c",
|
||||||
|
"sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c",
|
||||||
|
"sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341",
|
||||||
|
"sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f",
|
||||||
|
"sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7",
|
||||||
|
"sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d",
|
||||||
|
"sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007",
|
||||||
|
"sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92",
|
||||||
|
"sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb",
|
||||||
|
"sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5",
|
||||||
|
"sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5",
|
||||||
|
"sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8",
|
||||||
|
"sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1",
|
||||||
|
"sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68",
|
||||||
|
"sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73",
|
||||||
|
"sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1",
|
||||||
|
"sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53",
|
||||||
|
"sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d",
|
||||||
|
"sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906",
|
||||||
|
"sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0",
|
||||||
|
"sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2",
|
||||||
|
"sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a",
|
||||||
|
"sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b",
|
||||||
|
"sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44",
|
||||||
|
"sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648",
|
||||||
|
"sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7",
|
||||||
|
"sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f",
|
||||||
|
"sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa",
|
||||||
|
"sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697",
|
||||||
|
"sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d",
|
||||||
|
"sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b",
|
||||||
|
"sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526",
|
||||||
|
"sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4",
|
||||||
|
"sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287",
|
||||||
|
"sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e",
|
||||||
|
"sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673",
|
||||||
|
"sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0",
|
||||||
|
"sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30",
|
||||||
|
"sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3",
|
||||||
|
"sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e",
|
||||||
|
"sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92",
|
||||||
|
"sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a",
|
||||||
|
"sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c",
|
||||||
|
"sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8",
|
||||||
|
"sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909",
|
||||||
|
"sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47",
|
||||||
|
"sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864",
|
||||||
|
"sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc",
|
||||||
|
"sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00",
|
||||||
|
"sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb",
|
||||||
|
"sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539",
|
||||||
|
"sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b",
|
||||||
|
"sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481",
|
||||||
|
"sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5",
|
||||||
|
"sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4",
|
||||||
|
"sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64",
|
||||||
|
"sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392",
|
||||||
|
"sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4",
|
||||||
|
"sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1",
|
||||||
|
"sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1",
|
||||||
|
"sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567",
|
||||||
|
"sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==2.9.10"
|
||||||
|
},
|
||||||
|
"pyasn1": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629",
|
||||||
|
"sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==0.6.1"
|
||||||
|
},
|
||||||
|
"pyasn1-modules": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd",
|
||||||
|
"sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==0.4.1"
|
||||||
|
},
|
||||||
|
"pycparser": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6",
|
||||||
|
"sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==2.22"
|
||||||
|
},
|
||||||
|
"pyopenssl": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:49f7a019577d834746bc55c5fce6ecbcec0f2b4ec5ce1cf43a9a173b8138bb36",
|
||||||
|
"sha256:e474f5a473cd7f92221cc04976e48f4d11502804657a08a989fb3be5514c904a"
|
||||||
|
],
|
||||||
|
"version": "==24.3.0"
|
||||||
|
},
|
||||||
|
"redis": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f",
|
||||||
|
"sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==5.2.1"
|
||||||
|
},
|
||||||
|
"service-identity": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6b047fbd8a84fd0bb0d55ebce4031e400562b9196e1e0d3e0fe2b8a59f6d4a85",
|
||||||
|
"sha256:b8683ba13f0d39c6cd5d625d2c5f65421d6d707b013b375c355751557cbe8e09"
|
||||||
|
],
|
||||||
|
"version": "==24.2.0"
|
||||||
|
},
|
||||||
|
"setuptools": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6",
|
||||||
|
"sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.9'",
|
||||||
|
"version": "==75.6.0"
|
||||||
|
},
|
||||||
|
"six": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274",
|
||||||
|
"sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||||
|
"version": "==1.17.0"
|
||||||
|
},
|
||||||
|
"sqlparse": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272",
|
||||||
|
"sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==0.5.3"
|
||||||
|
},
|
||||||
|
"twisted": {
|
||||||
|
"extras": [
|
||||||
|
"http2",
|
||||||
|
"tls"
|
||||||
|
],
|
||||||
|
"hashes": [
|
||||||
|
"sha256:695d0556d5ec579dcc464d2856b634880ed1319f45b10d19043f2b57eb0115b5",
|
||||||
|
"sha256:fe403076c71f04d5d2d789a755b687c5637ec3bcd3b2b8252d76f2ba65f54261"
|
||||||
|
],
|
||||||
|
"markers": "python_full_version >= '3.8.0'",
|
||||||
|
"version": "==24.11.0"
|
||||||
|
},
|
||||||
|
"txaio": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:aaea42f8aad50e0ecfb976130ada140797e9dcb85fad2cf72b0f37f8cefcb490",
|
||||||
|
"sha256:f9a9216e976e5e3246dfd112ad7ad55ca915606b60b84a757ac769bd404ff704"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==23.1.1"
|
||||||
|
},
|
||||||
|
"typing-extensions": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d",
|
||||||
|
"sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==4.12.2"
|
||||||
|
},
|
||||||
|
"zope-interface": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:033b3923b63474800b04cba480b70f6e6243a62208071fc148354f3f89cc01b7",
|
||||||
|
"sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a",
|
||||||
|
"sha256:086ee2f51eaef1e4a52bd7d3111a0404081dadae87f84c0ad4ce2649d4f708b7",
|
||||||
|
"sha256:0ef9e2f865721553c6f22a9ff97da0f0216c074bd02b25cf0d3af60ea4d6931d",
|
||||||
|
"sha256:1090c60116b3da3bfdd0c03406e2f14a1ff53e5771aebe33fec1edc0a350175d",
|
||||||
|
"sha256:144964649eba4c5e4410bb0ee290d338e78f179cdbfd15813de1a664e7649b3b",
|
||||||
|
"sha256:15398c000c094b8855d7d74f4fdc9e73aa02d4d0d5c775acdef98cdb1119768d",
|
||||||
|
"sha256:1909f52a00c8c3dcab6c4fad5d13de2285a4b3c7be063b239b8dc15ddfb73bd2",
|
||||||
|
"sha256:21328fcc9d5b80768bf051faa35ab98fb979080c18e6f84ab3f27ce703bce465",
|
||||||
|
"sha256:224b7b0314f919e751f2bca17d15aad00ddbb1eadf1cb0190fa8175edb7ede62",
|
||||||
|
"sha256:25e6a61dcb184453bb00eafa733169ab6d903e46f5c2ace4ad275386f9ab327a",
|
||||||
|
"sha256:27f926f0dcb058211a3bb3e0e501c69759613b17a553788b2caeb991bed3b61d",
|
||||||
|
"sha256:29caad142a2355ce7cfea48725aa8bcf0067e2b5cc63fcf5cd9f97ad12d6afb5",
|
||||||
|
"sha256:2ad9913fd858274db8dd867012ebe544ef18d218f6f7d1e3c3e6d98000f14b75",
|
||||||
|
"sha256:31d06db13a30303c08d61d5fb32154be51dfcbdb8438d2374ae27b4e069aac40",
|
||||||
|
"sha256:3e0350b51e88658d5ad126c6a57502b19d5f559f6cb0a628e3dc90442b53dd98",
|
||||||
|
"sha256:3f6771d1647b1fc543d37640b45c06b34832a943c80d1db214a37c31161a93f1",
|
||||||
|
"sha256:4893395d5dd2ba655c38ceb13014fd65667740f09fa5bb01caa1e6284e48c0cd",
|
||||||
|
"sha256:52e446f9955195440e787596dccd1411f543743c359eeb26e9b2c02b077b0519",
|
||||||
|
"sha256:550f1c6588ecc368c9ce13c44a49b8d6b6f3ca7588873c679bd8fd88a1b557b6",
|
||||||
|
"sha256:72cd1790b48c16db85d51fbbd12d20949d7339ad84fd971427cf00d990c1f137",
|
||||||
|
"sha256:7bd449c306ba006c65799ea7912adbbfed071089461a19091a228998b82b1fdb",
|
||||||
|
"sha256:7dc5016e0133c1a1ec212fc87a4f7e7e562054549a99c73c8896fa3a9e80cbc7",
|
||||||
|
"sha256:802176a9f99bd8cc276dcd3b8512808716492f6f557c11196d42e26c01a69a4c",
|
||||||
|
"sha256:80ecf2451596f19fd607bb09953f426588fc1e79e93f5968ecf3367550396b22",
|
||||||
|
"sha256:8b49f1a3d1ee4cdaf5b32d2e738362c7f5e40ac8b46dd7d1a65e82a4872728fe",
|
||||||
|
"sha256:8e7da17f53e25d1a3bde5da4601e026adc9e8071f9f6f936d0fe3fe84ace6d54",
|
||||||
|
"sha256:a102424e28c6b47c67923a1f337ede4a4c2bba3965b01cf707978a801fc7442c",
|
||||||
|
"sha256:a19a6cc9c6ce4b1e7e3d319a473cf0ee989cbbe2b39201d7c19e214d2dfb80c7",
|
||||||
|
"sha256:a71a5b541078d0ebe373a81a3b7e71432c61d12e660f1d67896ca62d9628045b",
|
||||||
|
"sha256:baf95683cde5bc7d0e12d8e7588a3eb754d7c4fa714548adcd96bdf90169f021",
|
||||||
|
"sha256:cab15ff4832580aa440dc9790b8a6128abd0b88b7ee4dd56abacbc52f212209d",
|
||||||
|
"sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2",
|
||||||
|
"sha256:d3a8ffec2a50d8ec470143ea3d15c0c52d73df882eef92de7537e8ce13475e8a",
|
||||||
|
"sha256:e204937f67b28d2dca73ca936d3039a144a081fc47a07598d44854ea2a106239",
|
||||||
|
"sha256:eb23f58a446a7f09db85eda09521a498e109f137b85fb278edb2e34841055398",
|
||||||
|
"sha256:f6dd02ec01f4468da0f234da9d9c8545c5412fef80bc590cc51d8dd084138a89"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {}
|
||||||
|
}
|
0
backend/app/__init__.py
Normal file
0
backend/app/__init__.py
Normal file
6
backend/app/admin.py
Normal file
6
backend/app/admin.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from app.models import Order, Waiter, Meal
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
|
|
||||||
|
admin.site.register([Meal, Order, Waiter])
|
6
backend/app/apps.py
Normal file
6
backend/app/apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class AppConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'app'
|
3
backend/app/consumers.py
Normal file
3
backend/app/consumers.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from channels.db import database_sync_to_async
|
||||||
|
from channels.generic.websocket import AsyncJsonWebsocketConsumer
|
||||||
|
from django.urls import reverse
|
33
backend/app/migrations/0001_initial.py
Normal file
33
backend/app/migrations/0001_initial.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2024-12-12 14:12
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Waiter',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=120, verbose_name='Imię i nazwisko')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Order',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created_on', models.DateTimeField(auto_now_add=True, verbose_name='Utworzono')),
|
||||||
|
('updated_on', models.DateTimeField(auto_now=True, verbose_name='Zaktualizowano')),
|
||||||
|
('data', models.JSONField(default=dict, verbose_name='Dane zamówienia')),
|
||||||
|
('status', models.PositiveSmallIntegerField(choices=[(1, 'Zamówienie złożone'), (2, 'Zamówienie w trakcie przygotowywania'), (3, 'Zamówienie gotowe'), (4, 'Zamówienie skończone')], default=1, verbose_name='Status zamówienia')),
|
||||||
|
('waiter', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='orders', to='app.waiter', verbose_name='Kelner')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
20
backend/app/migrations/0002_meal.py
Normal file
20
backend/app/migrations/0002_meal.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2024-12-16 13:56
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('app', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Meal',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.TextField(verbose_name='Danie')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
0
backend/app/migrations/__init__.py
Normal file
0
backend/app/migrations/__init__.py
Normal file
54
backend/app/models.py
Normal file
54
backend/app/models.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
|
|
||||||
|
|
||||||
|
class Waiter(models.Model):
|
||||||
|
name = models.CharField(_('Imię i nazwisko'), max_length=120)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("Kelner")
|
||||||
|
verbose_name_plural = _("Kelnerzy")
|
||||||
|
|
||||||
|
|
||||||
|
class Meal(models.Model):
|
||||||
|
name = models.TextField(_('Danie'))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("Danie")
|
||||||
|
verbose_name_plural = _("Dania")
|
||||||
|
|
||||||
|
|
||||||
|
class Order(models.Model):
|
||||||
|
created_on = models.DateTimeField(_('Utworzono'), auto_now_add=True)
|
||||||
|
updated_on = models.DateTimeField(_('Zaktualizowano'), auto_now=True)
|
||||||
|
waiter = models.ForeignKey(
|
||||||
|
Waiter, models.SET_NULL, related_name='orders', verbose_name=_('Kelner'), null=True
|
||||||
|
)
|
||||||
|
data = models.JSONField(_('Dane zamówienia'), default=dict)
|
||||||
|
|
||||||
|
class StatusChoices(models.IntegerChoices):
|
||||||
|
ORDERED = (1, _('Zamówienie złożone'))
|
||||||
|
IN_PROGRESS = (2, ('Zamówienie w trakcie przygotowywania'))
|
||||||
|
READY = (3, _('Zamówienie gotowe'))
|
||||||
|
FINALIZED = (4, _('Zamówienie skończone'))
|
||||||
|
|
||||||
|
status = models.PositiveSmallIntegerField(
|
||||||
|
_('Status zamówienia'),
|
||||||
|
choices=StatusChoices.choices,
|
||||||
|
default=StatusChoices.ORDERED,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.id
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("Zamówienie")
|
||||||
|
verbose_name_plural = _("Zamówienia")
|
7
backend/app/routing.py
Normal file
7
backend/app/routing.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# app/routing.py
|
||||||
|
|
||||||
|
from django.urls import re_path
|
||||||
|
|
||||||
|
from . import consumers
|
||||||
|
|
||||||
|
websocket_urlpatterns = []
|
3
backend/app/tests.py
Normal file
3
backend/app/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
5
backend/app/views.py
Normal file
5
backend/app/views.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
|
|
||||||
|
|
0
backend/backend/__init__.py
Normal file
0
backend/backend/__init__.py
Normal file
34
backend/backend/asgi.py
Normal file
34
backend/backend/asgi.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
"""
|
||||||
|
ASGI config for backend project.
|
||||||
|
|
||||||
|
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import django
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from channels.auth import AuthMiddlewareStack
|
||||||
|
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||||
|
from channels.security.websocket import AllowedHostsOriginValidator
|
||||||
|
from django.core.asgi import get_asgi_application
|
||||||
|
|
||||||
|
from app.routing import websocket_urlpatterns
|
||||||
|
|
||||||
|
# Initialize Django ASGI application early to ensure the AppRegistry
|
||||||
|
# is populated before importing code that may import ORM models.
|
||||||
|
django_asgi_app = get_asgi_application()
|
||||||
|
|
||||||
|
application = ProtocolTypeRouter(
|
||||||
|
{
|
||||||
|
'http': django_asgi_app,
|
||||||
|
'websocket': AllowedHostsOriginValidator(
|
||||||
|
AuthMiddlewareStack(URLRouter(websocket_urlpatterns))
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
148
backend/backend/settings.py
Normal file
148
backend/backend/settings.py
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
"""
|
||||||
|
Django settings for backend project.
|
||||||
|
|
||||||
|
Generated by 'django-admin startproject' using Django 5.1.4.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/5.1/topics/settings/
|
||||||
|
|
||||||
|
For the full list of settings and their values, see
|
||||||
|
https://docs.djangoproject.com/en/5.1/ref/settings/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
# Quick-start development settings - unsuitable for production
|
||||||
|
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
SECRET_KEY = os.environ['SECRET_KEY']
|
||||||
|
|
||||||
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
|
DEBUG = os.environ['DEBUG']=='True'
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = ['*']
|
||||||
|
CSRF_TRUSTED_ORIGINS = ['http://127.0.0.1', 'https://localhost']
|
||||||
|
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django_browser_reload',
|
||||||
|
'app',
|
||||||
|
'daphne',
|
||||||
|
'rest_framework',
|
||||||
|
'django_filters',
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
'django_browser_reload.middleware.BrowserReloadMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'backend.urls'
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = 'backend.wsgi.application'
|
||||||
|
ASGI_APPLICATION = 'backend.asgi.application'
|
||||||
|
|
||||||
|
# Channel layers
|
||||||
|
|
||||||
|
CHANNEL_LAYERS = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'channels_redis.core.RedisChannelLayer',
|
||||||
|
'CONFIG': {
|
||||||
|
'hosts': [('redis', 6379)],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'NAME': 'db',
|
||||||
|
'USER': os.environ['DB_USER'],
|
||||||
|
'PASSWORD': os.environ['DB_PASSWORD'],
|
||||||
|
'HOST': 'db',
|
||||||
|
'PORT': 5432,
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Password validation
|
||||||
|
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Internationalization
|
||||||
|
# https://docs.djangoproject.com/en/5.1/topics/i18n/
|
||||||
|
|
||||||
|
LANGUAGE_CODE = 'pl-PL'
|
||||||
|
|
||||||
|
TIME_ZONE = 'Europe/Warsaw'
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
|
||||||
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
# https://docs.djangoproject.com/en/5.1/howto/static-files/
|
||||||
|
|
||||||
|
STATIC_ROOT = BASE_DIR / 'static'
|
||||||
|
STATIC_URL = 'static/'
|
||||||
|
|
||||||
|
# Default primary key field type
|
||||||
|
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
109
backend/backend/urls.py
Normal file
109
backend/backend/urls.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
"""
|
||||||
|
URL configuration for backend project.
|
||||||
|
|
||||||
|
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||||
|
https://docs.djangoproject.com/en/5.1/topics/http/urls/
|
||||||
|
Examples:
|
||||||
|
Function views
|
||||||
|
1. Add an import: from my_app import views
|
||||||
|
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||||
|
Class-based views
|
||||||
|
1. Add an import: from other_app.views import Home
|
||||||
|
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||||
|
Including another URLconf
|
||||||
|
1. Import the include() function: from django.urls import include, path
|
||||||
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.urls import path, include
|
||||||
|
from rest_framework import routers, serializers, viewsets, filters
|
||||||
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
|
from django.db.models import Q
|
||||||
|
from django.utils import timezone
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from app.models import Order, Waiter, Meal
|
||||||
|
|
||||||
|
|
||||||
|
# Serializers define the API representation.
|
||||||
|
class WaiterSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Waiter
|
||||||
|
fields = [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# ViewSets define the view behavior.
|
||||||
|
class WaiterViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = Waiter.objects.all()
|
||||||
|
serializer_class = WaiterSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class MealSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Meal
|
||||||
|
fields = [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# ViewSets define the view behavior.
|
||||||
|
class MealViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = Meal.objects.all()
|
||||||
|
serializer_class = MealSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class OrderSerializer(serializers.ModelSerializer):
|
||||||
|
waiter_name = serializers.SerializerMethodField()
|
||||||
|
status_name = serializers.CharField(source='get_status_display', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Order
|
||||||
|
fields = [
|
||||||
|
'id',
|
||||||
|
'created_on',
|
||||||
|
'updated_on',
|
||||||
|
'waiter',
|
||||||
|
'waiter_name',
|
||||||
|
'data',
|
||||||
|
'status',
|
||||||
|
'status_name',
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_waiter_name(self, obj):
|
||||||
|
return obj.waiter.name if obj.waiter else ''
|
||||||
|
|
||||||
|
|
||||||
|
# ViewSets define the view behavior.
|
||||||
|
class OrderViewSet(viewsets.ModelViewSet):
|
||||||
|
serializer_class = OrderSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
|
||||||
|
filterset_fields = ['waiter']
|
||||||
|
ordering = ['-updated_on']
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
five_minutes_ago = timezone.now() + datetime.timedelta(minutes=-5)
|
||||||
|
return Order.objects.filter(
|
||||||
|
~Q(status=Order.StatusChoices.FINALIZED)
|
||||||
|
| Q(updated_on__gte=five_minutes_ago)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Routers provide an easy way of automatically determining the URL conf.
|
||||||
|
router = routers.DefaultRouter()
|
||||||
|
router.register(r'meals', MealViewSet, 'meals')
|
||||||
|
router.register(r'waiters', WaiterViewSet, 'waiters')
|
||||||
|
router.register(r'orders', OrderViewSet, 'orders')
|
||||||
|
|
||||||
|
# Wire up our API using automatic URL routing.
|
||||||
|
# Additionally, we include login URLs for the browsable API.
|
||||||
|
urlpatterns = [
|
||||||
|
path('api/', include(router.urls)),
|
||||||
|
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
|
||||||
|
path('admin/', admin.site.urls),
|
||||||
|
path('__reload__/', include('django_browser_reload.urls')),
|
||||||
|
]
|
16
backend/backend/wsgi.py
Normal file
16
backend/backend/wsgi.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
WSGI config for backend project.
|
||||||
|
|
||||||
|
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
|
||||||
|
|
||||||
|
application = get_wsgi_application()
|
16
backend/entrypoint.sh
Executable file
16
backend/entrypoint.sh
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Waiting for postgres..."
|
||||||
|
|
||||||
|
while ! nc -z db 5432; do
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "PostgreSQL started"
|
||||||
|
|
||||||
|
# python manage.py flush --no-input
|
||||||
|
# python manage.py migrate
|
||||||
|
|
||||||
|
python manage.py collectstatic --no-input --clear > /dev/null 2>&1 &
|
||||||
|
|
||||||
|
exec "$@"
|
22
backend/manage.py
Executable file
22
backend/manage.py
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""Django's command-line utility for administrative tasks."""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Run administrative tasks."""
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
|
||||||
|
try:
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
except ImportError as exc:
|
||||||
|
raise ImportError(
|
||||||
|
"Couldn't import Django. Are you sure it's installed and "
|
||||||
|
"available on your PYTHONPATH environment variable? Did you "
|
||||||
|
"forget to activate a virtual environment?"
|
||||||
|
) from exc
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
12
backend/requirements.txt
Normal file
12
backend/requirements.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
Django
|
||||||
|
psycopg2-binary
|
||||||
|
django-browser-reload
|
||||||
|
six
|
||||||
|
channels[daphne]
|
||||||
|
channels_redis
|
||||||
|
redis
|
||||||
|
django-redis
|
||||||
|
Twisted[tls,http2]
|
||||||
|
djangorestframework
|
||||||
|
markdown
|
||||||
|
django-filter
|
49
docker-compose.yaml
Normal file
49
docker-compose.yaml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
services:
|
||||||
|
backend:
|
||||||
|
build: ./backend/
|
||||||
|
# command: daphne -b 0.0.0.0 -p 8000 backend.asgi:application
|
||||||
|
command: python manage.py runserver 0.0.0.0:8000
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./backend:/usr/src/app
|
||||||
|
- django_static:/usr/src/app/static
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
environment:
|
||||||
|
SECRET_KEY: ${SECRET_KEY}
|
||||||
|
DEBUG: ${DEBUG:-False}
|
||||||
|
DB_NAME: ${DB_NAME:-db}
|
||||||
|
DB_USER: ${DB_USER:-user}
|
||||||
|
DB_PASSWORD: ${DB_PASSWORD:-password}
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- db
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
dockerfile: Dockerfile.dev
|
||||||
|
command: pnpm run dev
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./frontend:/app
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
restart: unless-stopped
|
||||||
|
image: redis
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data/
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB=${DB_NAME:-db}
|
||||||
|
- POSTGRES_USER=${DB_USER:-user}
|
||||||
|
- POSTGRES_PASSWORD=${DB_PASSWORD:-password}
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
django_static:
|
60
docker-compose.yaml.prod
Normal file
60
docker-compose.yaml.prod
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
services:
|
||||||
|
backend:
|
||||||
|
build: ./backend/
|
||||||
|
command: daphne -b 0.0.0.0 -p 8000 backend.asgi:application
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- django_static_prod:/usr/src/app/static
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
environment:
|
||||||
|
SECRET_KEY: ${SECRET_KEY}
|
||||||
|
DEBUG: False
|
||||||
|
DB_NAME: ${DB_NAME:-db}
|
||||||
|
DB_USER: ${DB_USER:-user}
|
||||||
|
DB_PASSWORD: ${DB_PASSWORD:-password}
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- db
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
dockerfile: Dockerfile.prod
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./frontend:/app
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
restart: unless-stopped
|
||||||
|
image: redis
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- postgres_data_prod:/var/lib/postgresql/data/
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB=${DB_NAME:-db}
|
||||||
|
- POSTGRES_USER=${DB_USER:-user}
|
||||||
|
- POSTGRES_PASSWORD=${DB_PASSWORD:-password}
|
||||||
|
|
||||||
|
caddy:
|
||||||
|
image: caddy:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
# - "443:443"
|
||||||
|
# - "443:443/udp"
|
||||||
|
volumes:
|
||||||
|
- ./Caddyfile:/etc/caddy/Caddyfile
|
||||||
|
- django_static_prod:/static
|
||||||
|
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data_prod:
|
||||||
|
django_static_prod:
|
41
frontend/.gitignore
vendored
Normal file
41
frontend/.gitignore
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.*
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/versions
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# env files (can opt-in for committing if needed)
|
||||||
|
.env*
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
21
frontend/Dockerfile.dev
Normal file
21
frontend/Dockerfile.dev
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
FROM node:20 AS base
|
||||||
|
WORKDIR /app
|
||||||
|
RUN npm i -g pnpm
|
||||||
|
COPY package.json pnpm-lock.yaml ./
|
||||||
|
|
||||||
|
RUN pnpm install
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
FROM node:20-alpine3.19 as release
|
||||||
|
WORKDIR /app
|
||||||
|
RUN npm i -g pnpm
|
||||||
|
|
||||||
|
COPY --from=base /app/node_modules ./node_modules
|
||||||
|
COPY --from=base /app/package.json ./package.json
|
||||||
|
COPY --from=base /app/.next ./.next
|
||||||
|
COPY --from=base /app .
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD ["pnpm", "dev"]
|
65
frontend/Dockerfile.prod
Normal file
65
frontend/Dockerfile.prod
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
FROM node:alpine AS base
|
||||||
|
|
||||||
|
# Install dependencies only when needed
|
||||||
|
FROM base AS deps
|
||||||
|
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies based on the preferred package manager
|
||||||
|
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
|
||||||
|
RUN \
|
||||||
|
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
|
||||||
|
elif [ -f package-lock.json ]; then npm ci; \
|
||||||
|
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
|
||||||
|
else echo "Lockfile not found." && exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Rebuild the source code only when needed
|
||||||
|
FROM base AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Next.js collects completely anonymous telemetry data about general usage.
|
||||||
|
# Learn more here: https://nextjs.org/telemetry
|
||||||
|
# Uncomment the following line in case you want to disable telemetry during the build.
|
||||||
|
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
|
||||||
|
RUN yarn build
|
||||||
|
|
||||||
|
# If using npm comment out above and use below instead
|
||||||
|
# RUN npm run build
|
||||||
|
|
||||||
|
# Production image, copy all the files and run next
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV production
|
||||||
|
# Uncomment the following line in case you want to disable telemetry during runtime.
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
|
||||||
|
# Set the correct permission for prerender cache
|
||||||
|
RUN mkdir .next
|
||||||
|
RUN chown nextjs:nodejs .next
|
||||||
|
|
||||||
|
# Automatically leverage output traces to reduce image size
|
||||||
|
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV PORT 3000
|
||||||
|
# set hostname to localhost
|
||||||
|
ENV HOSTNAME "0.0.0.0"
|
||||||
|
|
||||||
|
CMD ["node", "server.js"]
|
10
frontend/app/api/meals/route.ts
Normal file
10
frontend/app/api/meals/route.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export async function GET() {
|
||||||
|
const res = await fetch('http://backend:8000/api/meals', {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
return Response.json(data);
|
||||||
|
}
|
42
frontend/app/api/orders/route.ts
Normal file
42
frontend/app/api/orders/route.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { NextRequest } from "next/server";
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest) {
|
||||||
|
const searchParams = request.nextUrl.searchParams
|
||||||
|
const query = searchParams.get('waiter')
|
||||||
|
const res = await fetch(!!query ? `http://backend:8000/api/orders/?waiter=${query}` : 'http://backend:8000/api/orders/', {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
return Response.json(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
const body = await request.json()
|
||||||
|
const res = await fetch('http://backend:8000/api/orders/', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return Response.json({ status: res.status });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function PUT(request: NextRequest) {
|
||||||
|
const searchParams = request.nextUrl.searchParams
|
||||||
|
const query = searchParams.get('id')
|
||||||
|
const body = await request.json()
|
||||||
|
const res = await fetch(`http://backend:8000/api/orders/${query}/`, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return Response.json({ status: res.status });
|
||||||
|
}
|
16
frontend/app/api/tools.ts
Normal file
16
frontend/app/api/tools.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import useSWR from 'swr'
|
||||||
|
|
||||||
|
export const fetcher = (...args) => fetch(...args).then(res => res.json()) // @ts-ignore
|
||||||
|
|
||||||
|
|
||||||
|
export function useUser (id: number) {
|
||||||
|
const { data, error, isLoading } = useSWR(`/api/waiters/${id}`, fetcher)
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: data,
|
||||||
|
isLoading,
|
||||||
|
isError: error
|
||||||
|
}
|
||||||
|
}
|
10
frontend/app/api/waiters/route.ts
Normal file
10
frontend/app/api/waiters/route.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export async function GET() {
|
||||||
|
const res = await fetch('http://backend:8000/api/waiters', {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
return Response.json(data);
|
||||||
|
}
|
BIN
frontend/app/favicon.ico
Normal file
BIN
frontend/app/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
48
frontend/app/globals.css
Normal file
48
frontend/app/globals.css
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
|
||||||
|
form#form p {
|
||||||
|
@apply flex flex-col;
|
||||||
|
}
|
||||||
|
|
||||||
|
form#form label {
|
||||||
|
@apply text-gray-700 font-bold text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
form#form input:not([type='checkbox']),
|
||||||
|
textarea {
|
||||||
|
@apply p-1 ps-2 transition ease-in-out focus:outline-none border-b-2 border-b-gray-400 mb-2 focus:bg-gray-100 focus:border-gray-600 appearance-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
form#form select {
|
||||||
|
@apply p-2 ps-2 rounded-md bg-gray-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
select.form {
|
||||||
|
@apply p-2 ps-2 rounded-md bg-gray-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown:focus-within .dropdown-menu {
|
||||||
|
/* @apply block; */
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer components {
|
||||||
|
button {
|
||||||
|
@apply std-btn
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose-with-table {
|
||||||
|
@apply prose-table:max-w-screen-2xl prose-table:text-center max-w-screen-xl w-full prose-td:py-1 prose-td:px-2 prose-th:py-1 prose-th:px-2 prose-td:align-middle prose-th:align-middle prose-thead:mx-auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.std-btn {
|
||||||
|
@apply inline-flex text-center justify-center items-center no-underline px-3 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-700 focus:outline-none focus:border-blue-300 active:bg-gray-50 active:text-gray-800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.std-menu-item {
|
||||||
|
@apply text-gray-700 flex justify-between w-full px-4 py-2 text-sm leading-5 text-left
|
||||||
|
}
|
||||||
|
}
|
106
frontend/app/kitchen/page.tsx
Normal file
106
frontend/app/kitchen/page.tsx
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { fetcher } from '@/app/api/tools';
|
||||||
|
import useSWR from 'swr';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
type Order = {
|
||||||
|
id: number;
|
||||||
|
created_on: string;
|
||||||
|
updated_on: string;
|
||||||
|
waiter: number;
|
||||||
|
waiter_name: string;
|
||||||
|
data: [{ item: string; additional_info: string; finished: boolean }];
|
||||||
|
status: string;
|
||||||
|
status_name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const { data, mutate, error, isLoading } = useSWR(`/api/orders`, fetcher, {
|
||||||
|
refreshInterval: 1000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const update_finished = (o: number, i: number, v: boolean) => {
|
||||||
|
let d = data;
|
||||||
|
d[o].data[i].finished = v;
|
||||||
|
console.log(v);
|
||||||
|
fetch(`/api/orders?id=${d[o].id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: JSON.stringify(d[o]),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
}).then(() => mutate(d));
|
||||||
|
};
|
||||||
|
|
||||||
|
const update_status = (o: number, v: number) => {
|
||||||
|
let d = data;
|
||||||
|
d[o].status = v;
|
||||||
|
console.log(v);
|
||||||
|
fetch(`/api/orders?id=${d[o].id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: JSON.stringify(d[o]),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
}).then(() => mutate(d));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (error) return <div>Błąd przy ładowaniu danych</div>;
|
||||||
|
if (isLoading) return <div>Ładowanie</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="text-4xl">Zamówienia:</p>
|
||||||
|
<ul className="max-w-screen-md w-full">
|
||||||
|
{data?.map((order: Order, oi: number) => (
|
||||||
|
<li key={order.id} className="m-2 p-2 border-2 bg-gray-100">
|
||||||
|
<h1 className="text-xl">
|
||||||
|
<span className="font-mono">
|
||||||
|
{'['}
|
||||||
|
{String(order.id % 1000).padStart(3, '0')}
|
||||||
|
{']'}
|
||||||
|
</span>{' '}
|
||||||
|
- {new Date(order.updated_on).toLocaleTimeString('pl-PL')}
|
||||||
|
</h1>
|
||||||
|
<h2 className="text-xl">{order.waiter_name}</h2>
|
||||||
|
<h3>
|
||||||
|
<select
|
||||||
|
value={order.status}
|
||||||
|
onChange={(e) =>
|
||||||
|
update_status(oi, e.target.value as unknown as number)
|
||||||
|
}
|
||||||
|
className='form'
|
||||||
|
>
|
||||||
|
<option value="1">Zamówienie złożone</option>
|
||||||
|
<option value="2">Zamówienie w trakcie przygotowywania</option>
|
||||||
|
<option value="3">Zamówienie gotowe</option>
|
||||||
|
<option value="4">Zamówienie skończone</option>
|
||||||
|
</select>
|
||||||
|
</h3>
|
||||||
|
<div className="">
|
||||||
|
<ul>
|
||||||
|
{order.data.map((dish, i) => (
|
||||||
|
<li
|
||||||
|
key={dish.item}
|
||||||
|
className="p-2 m-2 flex gap-4 bg-gray-200 border-2 border-gray-400"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={dish.finished}
|
||||||
|
onChange={(e) => update_finished(oi, i, e.target.checked)}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<p className="text-lg">{dish.item}</p>
|
||||||
|
<div>{dish.additional_info}</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
23
frontend/app/layout.tsx
Normal file
23
frontend/app/layout.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import "./globals.css";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Create Next App",
|
||||||
|
description: "Generated by create next app",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode;
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<html lang="pl">
|
||||||
|
<body
|
||||||
|
className={`antialiased p-4 flex flex-col gap-2 items-center`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
18
frontend/app/page.tsx
Normal file
18
frontend/app/page.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import Image from 'next/image';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className='text-xl'>Witamy</p>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<Link href={'/kitchen/'}>
|
||||||
|
<button>Kuchnia</button>
|
||||||
|
</Link>
|
||||||
|
<Link href={'/waiter/'}>
|
||||||
|
<button>Kelner</button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
147
frontend/app/waiter/[id]/new/page.tsx
Normal file
147
frontend/app/waiter/[id]/new/page.tsx
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { useForm, useFieldArray, useWatch, Control } from 'react-hook-form';
|
||||||
|
import { fetcher } from '@/app/api/tools';
|
||||||
|
import useSWR from 'swr';
|
||||||
|
|
||||||
|
type FormValues = {
|
||||||
|
data: {
|
||||||
|
item: string;
|
||||||
|
additional_info: string;
|
||||||
|
finished: boolean;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const { data, error, isLoading } = useSWR('/api/meals', fetcher);
|
||||||
|
|
||||||
|
const { id } = useParams();
|
||||||
|
const router = useRouter();
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm<FormValues>({
|
||||||
|
defaultValues: {
|
||||||
|
data: [{ item: '', additional_info: '', finished: false }],
|
||||||
|
},
|
||||||
|
mode: 'onBlur',
|
||||||
|
});
|
||||||
|
const { fields, append, remove } = useFieldArray({
|
||||||
|
name: 'data',
|
||||||
|
control,
|
||||||
|
});
|
||||||
|
const onSubmit = (data: FormValues) => {
|
||||||
|
console.log(data);
|
||||||
|
fetch('/api/orders', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ waiter: id, ...data }),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ status }) => console.log(status))
|
||||||
|
.then((_) => router.push(`/waiter/${id}`));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (error) return <div>Błąd przy ładowaniu danych</div>;
|
||||||
|
if (isLoading) return <div>Ładowanie</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<form
|
||||||
|
id="form"
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
className="flex flex-col max-w-screen-md w-full my-4"
|
||||||
|
>
|
||||||
|
<p className="text-2xl text-center">Nowe zamówienie</p>
|
||||||
|
{fields.map((field, index) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={field.id}
|
||||||
|
className="border-2 m-2 p-2 gap-4 flex flex-col items-stretch justify-around content-center"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="hidden"
|
||||||
|
{...register(`data.${index}.finished` as const, {
|
||||||
|
required: false,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<p className="basis-1/3">
|
||||||
|
<label htmlFor={`data.${index}.item`}>Danie</label>
|
||||||
|
<select
|
||||||
|
{...register(`data.${index}.item` as const, {
|
||||||
|
required: true,
|
||||||
|
})}
|
||||||
|
className={errors?.data?.[index]?.item ? 'error' : ''}
|
||||||
|
>
|
||||||
|
<option value="Inne">Inne</option>
|
||||||
|
{data?.map((data: { id: number; name: string }) => (
|
||||||
|
<option key={data.name} value={data.name}>
|
||||||
|
{data.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p className="basis-1/3">
|
||||||
|
<label htmlFor={`data.${index}.additional_info`}>
|
||||||
|
Dodatkowe informacje
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
placeholder="Dodatkowe informacje"
|
||||||
|
{...register(`data.${index}.additional_info` as const, {
|
||||||
|
required: false,
|
||||||
|
})}
|
||||||
|
className={
|
||||||
|
errors?.data?.[index]?.additional_info ? 'error' : ''
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
{fields.length > 1 ? (
|
||||||
|
<p className="basis-1/3 flex items-center justify-center">
|
||||||
|
<button type="button" onClick={() => remove(index)}>
|
||||||
|
Usuń
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="mx-auto"
|
||||||
|
onClick={() =>
|
||||||
|
append({
|
||||||
|
item: '',
|
||||||
|
additional_info: '',
|
||||||
|
finished: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Dodaj kolejne danie
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className={
|
||||||
|
'mt-10 mx-auto p-2 border-2 text-black hover:text-black ' +
|
||||||
|
(!!errors.data
|
||||||
|
? 'bg-red-300 border-red-500'
|
||||||
|
: 'bg-green-300 border-green-500')
|
||||||
|
}
|
||||||
|
disabled={!!errors.data}
|
||||||
|
>
|
||||||
|
Złóż zamówienie
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<Link href={`/waiter/${id}`} className='mt-10'><button>Powrót</button></Link>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
76
frontend/app/waiter/[id]/page.tsx
Normal file
76
frontend/app/waiter/[id]/page.tsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { fetcher } from '@/app/api/tools';
|
||||||
|
import useSWR from 'swr';
|
||||||
|
import { useParams } from 'next/navigation';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const { id } = useParams();
|
||||||
|
const { data, error, isLoading } = useSWR(
|
||||||
|
`/api/orders?waiter=${id}`,
|
||||||
|
fetcher,
|
||||||
|
{
|
||||||
|
refreshInterval: 1000,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) return <div>Błąd przy ładowaniu danych</div>;
|
||||||
|
if (isLoading) return <div>Ładowanie</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="text-lg">
|
||||||
|
<Link href={`/waiter/${id}/new`}>
|
||||||
|
<button>Nowe zamówienie</button>
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
<p className="text-4xl">Zamówienia:</p>
|
||||||
|
<ul className="max-w-screen-md w-full">
|
||||||
|
{data?.map(
|
||||||
|
(order: {
|
||||||
|
id: number;
|
||||||
|
created_on: string;
|
||||||
|
updated_on: string;
|
||||||
|
waiter: number;
|
||||||
|
waiter_name: string;
|
||||||
|
data: [
|
||||||
|
{ item: string; additional_info: string; finished: boolean }
|
||||||
|
];
|
||||||
|
status: string;
|
||||||
|
status_name: string;
|
||||||
|
}) => (
|
||||||
|
<li key={order.id} className="m-2 p-2 border-2 bg-gray-100">
|
||||||
|
<h1 className="text-xl">
|
||||||
|
<span className="font-mono">
|
||||||
|
{'['}
|
||||||
|
{String(order.id % 1000).padStart(3, '0')}
|
||||||
|
{']'}
|
||||||
|
</span>{' '}
|
||||||
|
- {new Date(order.updated_on).toLocaleTimeString('pl-PL')}
|
||||||
|
</h1>
|
||||||
|
<h2 className="text-xl">{order.waiter_name}</h2>
|
||||||
|
<h3>{order.status_name}</h3>
|
||||||
|
<div className="">
|
||||||
|
<ul>
|
||||||
|
{order.data.map((dish) => (
|
||||||
|
<li
|
||||||
|
key={dish.item}
|
||||||
|
className="p-2 m-2 flex gap-4 bg-gray-200 border-2 border-gray-400"
|
||||||
|
>
|
||||||
|
<input type="checkbox" checked={dish.finished} readOnly />
|
||||||
|
<div>
|
||||||
|
<p className="text-lg">{dish.item}</p>
|
||||||
|
<div>{dish.additional_info}</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
27
frontend/app/waiter/page.tsx
Normal file
27
frontend/app/waiter/page.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { fetcher } from '@/app/api/tools';
|
||||||
|
import useSWR from 'swr';
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const { data, error, isLoading } = useSWR('/api/waiters', fetcher);
|
||||||
|
|
||||||
|
if (error) return <div>Błąd przy ładowaniu danych</div>;
|
||||||
|
if (isLoading) return <div>Ładowanie</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="text-xl">Kelnerzy:</p>
|
||||||
|
<ul className="text-lg flex flex-col items-center gap-2">
|
||||||
|
{data?.map((data: { id: number; name: string }) => (
|
||||||
|
<li key={data.id}>
|
||||||
|
<Link href={`/waiter/${data.id}`}>
|
||||||
|
<button>{data.name}</button>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
16
frontend/eslint.config.mjs
Normal file
16
frontend/eslint.config.mjs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { dirname } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { FlatCompat } from "@eslint/eslintrc";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: __dirname,
|
||||||
|
});
|
||||||
|
|
||||||
|
const eslintConfig = [
|
||||||
|
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||||
|
];
|
||||||
|
|
||||||
|
export default eslintConfig;
|
7
frontend/next.config.ts
Normal file
7
frontend/next.config.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
|
const nextConfig: NextConfig = {
|
||||||
|
/* config options here */
|
||||||
|
};
|
||||||
|
|
||||||
|
export default nextConfig;
|
29
frontend/package.json
Normal file
29
frontend/package.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev --turbopack",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"next": "15.1.0",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0",
|
||||||
|
"react-hook-form": "^7.54.0",
|
||||||
|
"swr": "^2.2.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/eslintrc": "^3",
|
||||||
|
"@types/node": "^20",
|
||||||
|
"@types/react": "^19",
|
||||||
|
"@types/react-dom": "^19",
|
||||||
|
"eslint": "^9",
|
||||||
|
"eslint-config-next": "15.1.0",
|
||||||
|
"postcss": "^8",
|
||||||
|
"tailwindcss": "^3.4.1",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
3546
frontend/pnpm-lock.yaml
generated
Normal file
3546
frontend/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
8
frontend/postcss.config.mjs
Normal file
8
frontend/postcss.config.mjs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/** @type {import('postcss-load-config').Config} */
|
||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
19
frontend/tailwind.config.ts
Normal file
19
frontend/tailwind.config.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import type { Config } from "tailwindcss";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
darkMode: "class",
|
||||||
|
content: [
|
||||||
|
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
background: "var(--background)",
|
||||||
|
foreground: "var(--foreground)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
} satisfies Config;
|
27
frontend/tsconfig.json
Normal file
27
frontend/tsconfig.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
Reference in New Issue
Block a user