First version

This commit is contained in:
2024-12-16 16:35:12 +01:00
commit 69bc5698bd
52 changed files with 5598 additions and 0 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
frontend/node_modules

10
.env.example Normal file
View 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
View File

@ -0,0 +1 @@
.env

16
Caddyfile Normal file
View 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
View 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
View 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
View 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
View 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
View File

6
backend/app/admin.py Normal file
View 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
View 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
View File

@ -0,0 +1,3 @@
from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from django.urls import reverse

View 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')),
],
),
]

View 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')),
],
),
]

View File

54
backend/app/models.py Normal file
View 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
View 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
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

5
backend/app/views.py Normal file
View File

@ -0,0 +1,5 @@
from django.shortcuts import render
# Create your views here.

View File

34
backend/backend/asgi.py Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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"]

View 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);
}

View 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
View 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
}
}

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

48
frontend/app/globals.css Normal file
View 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
}
}

View 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
View 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
View 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>
</>
);
}

View 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>
</>
);
}

View 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>
</>
);
}

View 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>
</>
);
}

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

View 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
View 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"]
}

2
ruff.toml Normal file
View File

@ -0,0 +1,2 @@
[format]
quote-style = "single"