diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index f8f447cfb1abcfcd48166bba9730c1ee946c80a1..e5ce6d1b3b68c056267230680d73f9f84378c708 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -64,12 +64,13 @@
       }
     },
     "node_modules/@babel/code-frame": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz",
-      "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==",
+      "version": "7.23.5",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
+      "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
       "dev": true,
       "dependencies": {
-        "@babel/highlight": "^7.22.5"
+        "@babel/highlight": "^7.23.4",
+        "chalk": "^2.4.2"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -115,12 +116,12 @@
       }
     },
     "node_modules/@babel/generator": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz",
-      "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==",
+      "version": "7.23.6",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz",
+      "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==",
       "dev": true,
       "dependencies": {
-        "@babel/types": "^7.22.5",
+        "@babel/types": "^7.23.6",
         "@jridgewell/gen-mapping": "^0.3.2",
         "@jridgewell/trace-mapping": "^0.3.17",
         "jsesc": "^2.5.1"
@@ -184,22 +185,22 @@
       }
     },
     "node_modules/@babel/helper-environment-visitor": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz",
-      "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==",
+      "version": "7.22.20",
+      "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
+      "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
       "dev": true,
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/helper-function-name": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz",
-      "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==",
+      "version": "7.23.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
+      "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
       "dev": true,
       "dependencies": {
-        "@babel/template": "^7.22.5",
-        "@babel/types": "^7.22.5"
+        "@babel/template": "^7.22.15",
+        "@babel/types": "^7.23.0"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -335,18 +336,18 @@
       }
     },
     "node_modules/@babel/helper-string-parser": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
-      "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
+      "version": "7.23.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
+      "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
       "dev": true,
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz",
-      "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==",
+      "version": "7.22.20",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
+      "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
       "dev": true,
       "engines": {
         "node": ">=6.9.0"
@@ -376,13 +377,13 @@
       }
     },
     "node_modules/@babel/highlight": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz",
-      "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==",
+      "version": "7.23.4",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
+      "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
       "dev": true,
       "dependencies": {
-        "@babel/helper-validator-identifier": "^7.22.5",
-        "chalk": "^2.0.0",
+        "@babel/helper-validator-identifier": "^7.22.20",
+        "chalk": "^2.4.2",
         "js-tokens": "^4.0.0"
       },
       "engines": {
@@ -390,9 +391,9 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.22.6",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.6.tgz",
-      "integrity": "sha512-EIQu22vNkceq3LbjAq7knDf/UmtI2qbcNI8GRBlijez6TpQLvSodJPYfydQmNA5buwkxxxa/PVI44jjYZ+/cLw==",
+      "version": "7.23.9",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz",
+      "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==",
       "bin": {
         "parser": "bin/babel-parser.js"
       },
@@ -449,34 +450,34 @@
       }
     },
     "node_modules/@babel/template": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz",
-      "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==",
+      "version": "7.23.9",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz",
+      "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==",
       "dev": true,
       "dependencies": {
-        "@babel/code-frame": "^7.22.5",
-        "@babel/parser": "^7.22.5",
-        "@babel/types": "^7.22.5"
+        "@babel/code-frame": "^7.23.5",
+        "@babel/parser": "^7.23.9",
+        "@babel/types": "^7.23.9"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/traverse": {
-      "version": "7.22.6",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.6.tgz",
-      "integrity": "sha512-53CijMvKlLIDlOTrdWiHileRddlIiwUIyCKqYa7lYnnPldXCG5dUSN38uT0cA6i7rHWNKJLH0VU/Kxdr1GzB3w==",
+      "version": "7.23.9",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz",
+      "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==",
       "dev": true,
       "dependencies": {
-        "@babel/code-frame": "^7.22.5",
-        "@babel/generator": "^7.22.5",
-        "@babel/helper-environment-visitor": "^7.22.5",
-        "@babel/helper-function-name": "^7.22.5",
+        "@babel/code-frame": "^7.23.5",
+        "@babel/generator": "^7.23.6",
+        "@babel/helper-environment-visitor": "^7.22.20",
+        "@babel/helper-function-name": "^7.23.0",
         "@babel/helper-hoist-variables": "^7.22.5",
         "@babel/helper-split-export-declaration": "^7.22.6",
-        "@babel/parser": "^7.22.6",
-        "@babel/types": "^7.22.5",
-        "debug": "^4.1.0",
+        "@babel/parser": "^7.23.9",
+        "@babel/types": "^7.23.9",
+        "debug": "^4.3.1",
         "globals": "^11.1.0"
       },
       "engines": {
@@ -484,13 +485,13 @@
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz",
-      "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==",
+      "version": "7.23.9",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz",
+      "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==",
       "dev": true,
       "dependencies": {
-        "@babel/helper-string-parser": "^7.22.5",
-        "@babel/helper-validator-identifier": "^7.22.5",
+        "@babel/helper-string-parser": "^7.23.4",
+        "@babel/helper-validator-identifier": "^7.22.20",
         "to-fast-properties": "^2.0.0"
       },
       "engines": {
@@ -498,9 +499,9 @@
       }
     },
     "node_modules/@esbuild/android-arm": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz",
-      "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
+      "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
       "cpu": [
         "arm"
       ],
@@ -514,9 +515,9 @@
       }
     },
     "node_modules/@esbuild/android-arm64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz",
-      "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
+      "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
       "cpu": [
         "arm64"
       ],
@@ -530,9 +531,9 @@
       }
     },
     "node_modules/@esbuild/android-x64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz",
-      "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
+      "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
       "cpu": [
         "x64"
       ],
@@ -546,9 +547,9 @@
       }
     },
     "node_modules/@esbuild/darwin-arm64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz",
-      "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
+      "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
       "cpu": [
         "arm64"
       ],
@@ -562,9 +563,9 @@
       }
     },
     "node_modules/@esbuild/darwin-x64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz",
-      "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
+      "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
       "cpu": [
         "x64"
       ],
@@ -578,9 +579,9 @@
       }
     },
     "node_modules/@esbuild/freebsd-arm64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz",
-      "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
+      "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
       "cpu": [
         "arm64"
       ],
@@ -594,9 +595,9 @@
       }
     },
     "node_modules/@esbuild/freebsd-x64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz",
-      "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
+      "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
       "cpu": [
         "x64"
       ],
@@ -610,9 +611,9 @@
       }
     },
     "node_modules/@esbuild/linux-arm": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz",
-      "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
+      "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
       "cpu": [
         "arm"
       ],
@@ -626,9 +627,9 @@
       }
     },
     "node_modules/@esbuild/linux-arm64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz",
-      "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
+      "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
       "cpu": [
         "arm64"
       ],
@@ -642,9 +643,9 @@
       }
     },
     "node_modules/@esbuild/linux-ia32": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz",
-      "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
+      "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
       "cpu": [
         "ia32"
       ],
@@ -658,9 +659,9 @@
       }
     },
     "node_modules/@esbuild/linux-loong64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz",
-      "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
+      "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
       "cpu": [
         "loong64"
       ],
@@ -674,9 +675,9 @@
       }
     },
     "node_modules/@esbuild/linux-mips64el": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz",
-      "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
+      "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
       "cpu": [
         "mips64el"
       ],
@@ -690,9 +691,9 @@
       }
     },
     "node_modules/@esbuild/linux-ppc64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz",
-      "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
+      "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
       "cpu": [
         "ppc64"
       ],
@@ -706,9 +707,9 @@
       }
     },
     "node_modules/@esbuild/linux-riscv64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz",
-      "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
+      "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
       "cpu": [
         "riscv64"
       ],
@@ -722,9 +723,9 @@
       }
     },
     "node_modules/@esbuild/linux-s390x": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz",
-      "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
+      "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
       "cpu": [
         "s390x"
       ],
@@ -738,9 +739,9 @@
       }
     },
     "node_modules/@esbuild/linux-x64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz",
-      "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
+      "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
       "cpu": [
         "x64"
       ],
@@ -754,9 +755,9 @@
       }
     },
     "node_modules/@esbuild/netbsd-x64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz",
-      "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
+      "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
       "cpu": [
         "x64"
       ],
@@ -770,9 +771,9 @@
       }
     },
     "node_modules/@esbuild/openbsd-x64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz",
-      "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
+      "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
       "cpu": [
         "x64"
       ],
@@ -786,9 +787,9 @@
       }
     },
     "node_modules/@esbuild/sunos-x64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz",
-      "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
+      "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
       "cpu": [
         "x64"
       ],
@@ -802,9 +803,9 @@
       }
     },
     "node_modules/@esbuild/win32-arm64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz",
-      "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
+      "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
       "cpu": [
         "arm64"
       ],
@@ -818,9 +819,9 @@
       }
     },
     "node_modules/@esbuild/win32-ia32": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz",
-      "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
+      "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
       "cpu": [
         "ia32"
       ],
@@ -834,9 +835,9 @@
       }
     },
     "node_modules/@esbuild/win32-x64": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz",
-      "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
+      "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
       "cpu": [
         "x64"
       ],
@@ -1968,9 +1969,9 @@
       "optional": true
     },
     "node_modules/esbuild": {
-      "version": "0.17.18",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz",
-      "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==",
+      "version": "0.18.20",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
+      "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
       "dev": true,
       "hasInstallScript": true,
       "bin": {
@@ -1980,28 +1981,28 @@
         "node": ">=12"
       },
       "optionalDependencies": {
-        "@esbuild/android-arm": "0.17.18",
-        "@esbuild/android-arm64": "0.17.18",
-        "@esbuild/android-x64": "0.17.18",
-        "@esbuild/darwin-arm64": "0.17.18",
-        "@esbuild/darwin-x64": "0.17.18",
-        "@esbuild/freebsd-arm64": "0.17.18",
-        "@esbuild/freebsd-x64": "0.17.18",
-        "@esbuild/linux-arm": "0.17.18",
-        "@esbuild/linux-arm64": "0.17.18",
-        "@esbuild/linux-ia32": "0.17.18",
-        "@esbuild/linux-loong64": "0.17.18",
-        "@esbuild/linux-mips64el": "0.17.18",
-        "@esbuild/linux-ppc64": "0.17.18",
-        "@esbuild/linux-riscv64": "0.17.18",
-        "@esbuild/linux-s390x": "0.17.18",
-        "@esbuild/linux-x64": "0.17.18",
-        "@esbuild/netbsd-x64": "0.17.18",
-        "@esbuild/openbsd-x64": "0.17.18",
-        "@esbuild/sunos-x64": "0.17.18",
-        "@esbuild/win32-arm64": "0.17.18",
-        "@esbuild/win32-ia32": "0.17.18",
-        "@esbuild/win32-x64": "0.17.18"
+        "@esbuild/android-arm": "0.18.20",
+        "@esbuild/android-arm64": "0.18.20",
+        "@esbuild/android-x64": "0.18.20",
+        "@esbuild/darwin-arm64": "0.18.20",
+        "@esbuild/darwin-x64": "0.18.20",
+        "@esbuild/freebsd-arm64": "0.18.20",
+        "@esbuild/freebsd-x64": "0.18.20",
+        "@esbuild/linux-arm": "0.18.20",
+        "@esbuild/linux-arm64": "0.18.20",
+        "@esbuild/linux-ia32": "0.18.20",
+        "@esbuild/linux-loong64": "0.18.20",
+        "@esbuild/linux-mips64el": "0.18.20",
+        "@esbuild/linux-ppc64": "0.18.20",
+        "@esbuild/linux-riscv64": "0.18.20",
+        "@esbuild/linux-s390x": "0.18.20",
+        "@esbuild/linux-x64": "0.18.20",
+        "@esbuild/netbsd-x64": "0.18.20",
+        "@esbuild/openbsd-x64": "0.18.20",
+        "@esbuild/sunos-x64": "0.18.20",
+        "@esbuild/win32-arm64": "0.18.20",
+        "@esbuild/win32-ia32": "0.18.20",
+        "@esbuild/win32-x64": "0.18.20"
       }
     },
     "node_modules/escalade": {
@@ -3305,9 +3306,9 @@
       "optional": true
     },
     "node_modules/nanoid": {
-      "version": "3.3.6",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
-      "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+      "version": "3.3.7",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+      "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
       "funding": [
         {
           "type": "github",
@@ -3599,9 +3600,9 @@
       }
     },
     "node_modules/postcss": {
-      "version": "8.4.23",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
-      "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==",
+      "version": "8.4.33",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
+      "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
       "funding": [
         {
           "type": "opencollective",
@@ -3617,7 +3618,7 @@
         }
       ],
       "dependencies": {
-        "nanoid": "^3.3.6",
+        "nanoid": "^3.3.7",
         "picocolors": "^1.0.0",
         "source-map-js": "^1.0.2"
       },
@@ -3898,9 +3899,9 @@
       }
     },
     "node_modules/rollup": {
-      "version": "3.21.6",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.6.tgz",
-      "integrity": "sha512-SXIICxvxQxR3D4dp/3LDHZIJPC8a4anKMHd4E3Jiz2/JnY+2bEjqrOokAauc5ShGVNFHlEFjBXAXlaxkJqIqSg==",
+      "version": "3.29.4",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
+      "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
       "dev": true,
       "bin": {
         "rollup": "dist/bin/rollup"
@@ -4425,14 +4426,14 @@
       "devOptional": true
     },
     "node_modules/vite": {
-      "version": "4.3.9",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",
-      "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==",
+      "version": "4.5.2",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz",
+      "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==",
       "dev": true,
       "dependencies": {
-        "esbuild": "^0.17.5",
-        "postcss": "^8.4.23",
-        "rollup": "^3.21.0"
+        "esbuild": "^0.18.10",
+        "postcss": "^8.4.27",
+        "rollup": "^3.27.1"
       },
       "bin": {
         "vite": "bin/vite.js"
@@ -4440,12 +4441,16 @@
       "engines": {
         "node": "^14.18.0 || >=16.0.0"
       },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
       "optionalDependencies": {
         "fsevents": "~2.3.2"
       },
       "peerDependencies": {
         "@types/node": ">= 14",
         "less": "*",
+        "lightningcss": "^1.21.0",
         "sass": "*",
         "stylus": "*",
         "sugarss": "*",
@@ -4458,6 +4463,9 @@
         "less": {
           "optional": true
         },
+        "lightningcss": {
+          "optional": true
+        },
         "sass": {
           "optional": true
         },
diff --git a/frontend/src/colors.js b/frontend/src/colors.js
index bc03a40617f76778f8d8c4ea334a09dfbf6c2333..9b65e35987f619daba8cc8ef3e22220b7ad50ea4 100644
--- a/frontend/src/colors.js
+++ b/frontend/src/colors.js
@@ -14,6 +14,14 @@ const COLORS = {
     gray1: {
         name: 'Šedá 1',
         value: '#b2b2b2'
+    },
+    gray2: {
+        name: 'Šedá 2',
+        value: '#888888'
+    },
+    gray3: {
+        name: 'Šedá 3',
+        value: '#404040'
     }
 }
 
diff --git a/frontend/src/components/inputs/text/ShortTextInput.vue b/frontend/src/components/inputs/text/ShortTextInput.vue
index 6a35e7da14ef9e4561a128e1774e62f40f8bf662..94c051b414ee2e185d8d9b12ce1723d909cd37b8 100644
--- a/frontend/src/components/inputs/text/ShortTextInput.vue
+++ b/frontend/src/components/inputs/text/ShortTextInput.vue
@@ -6,7 +6,7 @@ import { sanitizeValue } from './utils'
 <script>
 export default {
     components: { InputHeading },
-    props: ['name', 'important', 'zIndex', 'relatedModel', 'predefinedValues', 'modelValue'],
+    props: ['name', 'important', 'zIndex', 'relatedModel', 'predefinedValues', 'modelValue', 'defaultValue'],
     emits: ['update:modelValue', 'update:relatedModel'],
     methods: {
         emitChanges (event) {
@@ -47,8 +47,8 @@ export default {
         <input
             class="p-2 font-condensed bg-gray-200 rounded-sm"
             type="text"
-            list="predefinedValues"
-            :value="modelValue"
+            :list="($props.predefinedValues) ? predefinedValues : ''"
+            :value="(modelValue !== null) ? modelValue : defaultValue"
             @input="emitChanges($event)"
         >
         <datalist
diff --git a/frontend/src/contractors.js b/frontend/src/contractors.js
new file mode 100644
index 0000000000000000000000000000000000000000..42c94c784d81979e865c8622b0787f45fb7c6a23
--- /dev/null
+++ b/frontend/src/contractors.js
@@ -0,0 +1,3 @@
+const DEFAULT_CONTRACTOR = "Zadavatel | zpracovatel: Česká Pirátská Strana"
+
+export default DEFAULT_CONTRACTOR
diff --git a/frontend/src/views/basic_photo_banner/BasicPhotoBanner.vue b/frontend/src/views/basic_photo_banner/BasicPhotoBanner.vue
index 6ea36cdc7f67edc6e5e50b8d725288f20ec87334..db708bfd2a94417d5c14e7fd40c972a5271c6880 100644
--- a/frontend/src/views/basic_photo_banner/BasicPhotoBanner.vue
+++ b/frontend/src/views/basic_photo_banner/BasicPhotoBanner.vue
@@ -4,6 +4,7 @@ import { watch, ref } from 'vue'
 import COLORS from '../../colors'
 import PEOPLE from '../../people'
 import TEMPLATES from '../../templates'
+import DEFAULT_CONTRACTOR from '../../contractors'
 import { generateDefaultLogos } from '../../logos'
 import { loadFonts, loadCanvasStorage, setCanvasStorage, updateAutoRedrawStorage } from '../../utils'
 
@@ -23,7 +24,7 @@ import AutoReloadCheckbox from '../../components/reload/AutoReloadCheckbox.vue'
 </script>
 
 <script>
-loadFonts([
+await loadFonts([
     '12px Bebas Neue',
     '12px Roboto Condensed',
     'bold 12px Roboto Condensed'
@@ -50,7 +51,8 @@ export default {
                     highlight: COLORS.yellow1,
                     arrow: COLORS.yellow1,
                     baseText: COLORS.white,
-                    highlightedText: COLORS.black
+                    highlightedText: COLORS.black,
+                    contractedByText: COLORS.gray1
                 }
             }
         }
@@ -60,6 +62,7 @@ export default {
             mainText: null,
             personName: null,
             personPosition: null,
+            contractedBy: DEFAULT_CONTRACTOR,
             logoImage: null,
             gradientHeightMultiplier: 1,
             colorLabels: {
@@ -85,6 +88,7 @@ export default {
                 mainText: this.mainText,
                 personName: this.personName,
                 personPosition: this.personPosition,
+                contractedBy: this.contractedBy,
                 logoImage: this.logoImage,
                 gradientHeightMultiplier: this.gradientHeightMultiplier,
                 colors: this.colors
@@ -103,6 +107,7 @@ export default {
                 vm.mainText,
                 vm.personName,
                 vm.personPosition,
+                vm.contractedBy,
                 vm.logoImage,
                 vm.gradientHeightMultiplier,
                 vm.colors
@@ -214,6 +219,14 @@ export default {
                     :defaultPredefinedColors="predefinedColors.base"
                     zIndex="5"
                 ></MultipleColorPicker>
+
+                <ShortTextInput
+                    name="Zadavatel a zpracovatel"
+                    v-model="contractedBy"
+                    :defaultValue="DEFAULT_CONTRACTOR"
+                    :important="false"
+                    zIndex="4"
+                />
             </template>
         </MainContainer>
     </main>
diff --git a/frontend/src/views/basic_photo_banner/canvas.js b/frontend/src/views/basic_photo_banner/canvas.js
index 28499e81016bd04b94bb1d8d1ab0bc15e45d0244..33233f27fd0a809b4eedbe468e7e0907ab200521 100644
--- a/frontend/src/views/basic_photo_banner/canvas.js
+++ b/frontend/src/views/basic_photo_banner/canvas.js
@@ -12,6 +12,8 @@ let personPositionText = null
 let mainImage = null
 let logoImage = null
 
+let contractedByTextbox = null
+
 let arrow = null
 
 let mainImageSource = null
@@ -24,6 +26,7 @@ const redraw = async (canvas, options) => {
             personNameText,
             personInfoSeparator,
             personPositionText,
+            contractedByTextbox,
             arrow
         ],
         canvas
@@ -47,6 +50,10 @@ const redraw = async (canvas, options) => {
     const positionTextSeparatorWidth = Math.ceil(canvas.width * 0.0035)
     const positionTextMaxWidth = Math.ceil(canvas.width * 0.4)
 
+    const contractedByTextSize = Math.ceil(canvas.height * 0.02)
+    const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9)
+    const contractedByTextSidesMargin = Math.ceil(canvas.width * 0.03)
+
     const arrowWidth = Math.ceil(canvas.width * 0.047)
     const arrowHeight = Math.ceil(canvas.width * 0.055)
     const arrowThickness = Math.ceil(canvas.width * 0.019)
@@ -319,6 +326,36 @@ const redraw = async (canvas, options) => {
     /* END Logo render */
 
 
+    /* BEGIN Contracted by render */
+
+    if (options.contractedBy !== null) {
+        contractedByTextbox = new fabric.Textbox(
+            options.contractedBy,
+            {
+                left: canvas.width - contractedByTextMaxWidth - contractedByTextSidesMargin,
+                top: (
+                    canvas.height
+                    - contractedByTextSidesMargin
+                    - contractedByTextSize
+                ),
+                width: contractedByTextMaxWidth,
+                fontFamily: 'Roboto Condensed',
+                fontSize: contractedByTextSize,
+                textAlign: 'right',
+                fill: options.colors.contractedByText.value,
+                selectable: false,
+                zIndex: 10
+            }
+        )
+
+        checkTextBoxHeight(contractedByTextbox, 1)
+
+        canvas.add(contractedByTextbox)
+    }
+
+    /* END Contracted by render */
+
+
     /* BEGIN Main image render */
 
     if (
diff --git a/frontend/src/views/facebook_survey/FacebookSurvey.vue b/frontend/src/views/facebook_survey/FacebookSurvey.vue
index 309530cd7185018f610785c9c5241b0eb57993c9..9b8b9a6f6def00d0b76543517ca1bfe993d2bb6b 100644
--- a/frontend/src/views/facebook_survey/FacebookSurvey.vue
+++ b/frontend/src/views/facebook_survey/FacebookSurvey.vue
@@ -3,6 +3,7 @@ import { watch, ref } from 'vue'
 
 import COLORS from '../../colors'
 import TEMPLATES from '../../templates'
+import DEFAULT_CONTRACTOR from '../../contractors'
 import { generateDefaultLogos } from '../../logos'
 import { loadFonts, loadCanvasStorage, setCanvasStorage, updateAutoRedrawStorage } from '../../utils'
 
@@ -23,8 +24,9 @@ import AutoReloadCheckbox from '../../components/reload/AutoReloadCheckbox.vue'
 </script>
 
 <script>
-loadFonts([
+await loadFonts([
     '12px Bebas Neue',
+    '12px Roboto Condensed',
 ])
 
 export default {
@@ -48,7 +50,8 @@ export default {
                     highlight: COLORS.yellow1,
                     arrow: COLORS.yellow1,
                     baseText: COLORS.white,
-                    highlightedText: COLORS.black
+                    highlightedText: COLORS.black,
+                    contractedByText: COLORS.gray1
                 }
             }
         }
@@ -61,6 +64,7 @@ export default {
             firstEmojiText: null,
             secondEmojiImage: null,
             secondEmojiText: null,
+            contractedBy: DEFAULT_CONTRACTOR,
             colorLabels: {
                 background: 'Pozadí',
                 highlight: 'Zvýraznění',
@@ -83,6 +87,7 @@ export default {
             const canvasProperties = {
                 mainImage: this.mainImage,
                 mainText: this.mainText,
+                contractedBy: this.contractedBy,
                 logoImage: this.logoImage,
                 colors: this.colors,
                 firstEmojiImage: this.firstEmojiImage,
@@ -103,6 +108,7 @@ export default {
             vm => [
                 vm.mainImage,
                 vm.mainText,
+                vm.contractedBy,
                 vm.logoImage,
                 vm.colors,
                 vm.firstEmojiImage,
@@ -231,6 +237,14 @@ export default {
                     :defaultPredefinedColors="predefinedColors.base"
                     zIndex="3"
                 ></MultipleColorPicker>
+
+                <ShortTextInput
+                    name="Zadavatel a zpracovatel"
+                    v-model="contractedBy"
+                    :defaultValue="DEFAULT_CONTRACTOR"
+                    :important="false"
+                    zIndex="4"
+                />
             </template>
         </MainContainer>
     </main>
diff --git a/frontend/src/views/facebook_survey/canvas.js b/frontend/src/views/facebook_survey/canvas.js
index 90f05ad7c7b73e27cdce0c13943741dcf936f2ff..ee28452932008123f91fff4151b2221cdfd946ab 100644
--- a/frontend/src/views/facebook_survey/canvas.js
+++ b/frontend/src/views/facebook_survey/canvas.js
@@ -8,6 +8,8 @@ import { PaddedHighlightingTextbox } from '../../components/canvas/textbox'
 let mainTextBox = null
 let mainTextBoxBackground = null
 
+let contractedByTextbox = null
+
 let mainImage = null
 let logoImage = null
 
@@ -28,6 +30,7 @@ const redraw = async (canvas, options) => {
             firstEmojiText,
             secondEmojiImage,
             secondEmojiText,
+            contractedByTextbox,
         ],
         canvas
     )
@@ -41,6 +44,10 @@ const redraw = async (canvas, options) => {
     const mainTextSize = Math.ceil(canvas.height * 0.0725)
     const mainTextLineHeight = 1
 
+    const contractedByTextSize = Math.ceil(canvas.height * 0.02)
+    const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9)
+    const contractedByTextSidesMargin = Math.ceil(canvas.width * 0.03)
+
     const emojiImageHeight = Math.ceil(canvas.height * 0.100)
 
     const firstEmojiImageMarginSide = Math.ceil(canvas.width * 0.25)
@@ -336,6 +343,36 @@ const redraw = async (canvas, options) => {
     /* END Logo render */
 
 
+    /* BEGIN Contracted by render */
+
+    if (options.contractedBy !== null) {
+        contractedByTextbox = new fabric.Textbox(
+            options.contractedBy,
+            {
+                left: canvas.width - contractedByTextMaxWidth - contractedByTextSidesMargin,
+                top: (
+                    canvas.height
+                    - contractedByTextSidesMargin
+                    - contractedByTextSize
+                ),
+                width: contractedByTextMaxWidth,
+                fontFamily: 'Roboto Condensed',
+                fontSize: contractedByTextSize,
+                textAlign: 'right',
+                fill: options.colors.contractedByText.value,
+                selectable: false,
+                zIndex: 10
+            }
+        )
+
+        checkTextBoxHeight(contractedByTextbox, 1)
+
+        canvas.add(contractedByTextbox)
+    }
+
+    /* END Contracted by render */
+
+
     /* BEGIN Main image render */
 
     if (
diff --git a/frontend/src/views/newspaper_quote_bottom/NewspaperQuoteBottom.vue b/frontend/src/views/newspaper_quote_bottom/NewspaperQuoteBottom.vue
index b8177e04b02c7a3120ddd70954fe255cc6224f67..faf64a0d65be4be4343efd4880ef7a045f1dbf8b 100644
--- a/frontend/src/views/newspaper_quote_bottom/NewspaperQuoteBottom.vue
+++ b/frontend/src/views/newspaper_quote_bottom/NewspaperQuoteBottom.vue
@@ -4,6 +4,7 @@ import { watch, ref } from 'vue'
 import COLORS from '../../colors'
 import PEOPLE from '../../people'
 import TEMPLATES from '../../templates'
+import DEFAULT_CONTRACTOR from '../../contractors'
 import { generateDefaultLogos } from '../../logos'
 import { SOURCE_IMAGES } from '../utils/newspaper_quotes'
 import { loadFonts, loadCanvasStorage, setCanvasStorage, updateAutoRedrawStorage } from '../../utils'
@@ -23,7 +24,7 @@ import AutoReloadCheckbox from '../../components/reload/AutoReloadCheckbox.vue'
 </script>
 
 <script>
-loadFonts([
+await loadFonts([
     '700 12px Glegoo',
     '12px Roboto Condensed',
     'bold 12px Roboto Condensed'
@@ -49,7 +50,8 @@ export default {
                     highlight: COLORS.yellow1,
                     quotes: COLORS.yellow1,
                     baseText: COLORS.white,
-                    highlightedText: COLORS.black
+                    highlightedText: COLORS.black,
+                    contractedByText: COLORS.gray1
                 }
             },
             gray: {
@@ -70,6 +72,7 @@ export default {
             mainText: null,
             personName: null,
             personPosition: null,
+            contractedBy: DEFAULT_CONTRACTOR,
             logoImage: null,
             colorLabels: {
                 background: 'Pozadí',
@@ -96,6 +99,7 @@ export default {
                 mainText: this.mainText,
                 personName: this.personName,
                 personPosition: this.personPosition,
+                contractedBy: this.contractedBy,
                 logoImage: this.logoImage,
                 colors: this.colors
             }
@@ -114,6 +118,7 @@ export default {
                 vm.mainText,
                 vm.personName,
                 vm.personPosition,
+                vm.contractedBy,
                 vm.logoImage,
                 vm.colors
             ],
@@ -250,6 +255,14 @@ export default {
                     :defaultPredefinedColors="predefinedColors.base"
                     zIndex="4"
                 ></MultipleColorPicker>
+
+                <ShortTextInput
+                    name="Zadavatel a zpracovatel"
+                    v-model="contractedBy"
+                    :defaultValue="DEFAULT_CONTRACTOR"
+                    :important="false"
+                    zIndex="4"
+                />
             </template>
         </MainContainer>
     </main>
diff --git a/frontend/src/views/newspaper_quote_bottom/canvas.js b/frontend/src/views/newspaper_quote_bottom/canvas.js
index c7ac5b193f52ba38455b29da2d08709ef6d0e1ca..018e7b753b561e52123d5f8ee746a583f71d3fde 100644
--- a/frontend/src/views/newspaper_quote_bottom/canvas.js
+++ b/frontend/src/views/newspaper_quote_bottom/canvas.js
@@ -13,6 +13,8 @@ let personInfoSeparator = null
 let personPositionText = null
 let sourceText = null
 
+let contractedByTextbox = null
+
 let mainImage = null
 let logoImage = null
 let quoteImage = null
@@ -35,6 +37,7 @@ const redraw = async (canvas, options) => {
             personInfoSeparator,
             personNameText,
             personPositionText,
+            contractedByTextbox,
             leftQuote,
             rightQuote,
             tear,
@@ -58,6 +61,10 @@ const redraw = async (canvas, options) => {
     const bottomTextSize = Math.ceil(canvas.height * 0.03)
     const additionalContentExtraBottomMargin = Math.ceil(canvas.height * 0.1)
 
+    const contractedByTextSize = Math.ceil(canvas.height * 0.02)
+    const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9)
+    const contractedByTextSidesMargin = Math.ceil(canvas.width * 0.03)
+
     const logoWidth = Math.ceil(canvas.width * 0.2)
     const logoSideMargin = Math.ceil(canvas.width * 0.07)
 
@@ -510,6 +517,36 @@ const redraw = async (canvas, options) => {
     /* END Logo render */
 
 
+    /* BEGIN Contracted by render */
+
+    if (options.contractedBy !== null) {
+        contractedByTextbox = new fabric.Textbox(
+            options.contractedBy,
+            {
+                left: canvas.width - contractedByTextMaxWidth - contractedByTextSidesMargin,
+                top: (
+                    canvas.height
+                    - contractedByTextSidesMargin
+                    - contractedByTextSize
+                ),
+                width: contractedByTextMaxWidth,
+                fontFamily: 'Roboto Condensed',
+                fontSize: contractedByTextSize,
+                textAlign: 'right',
+                fill: options.colors.contractedByText.value,
+                selectable: false,
+                zIndex: 10
+            }
+        )
+
+        checkTextBoxHeight(contractedByTextbox, 1)
+
+        canvas.add(contractedByTextbox)
+    }
+
+    /* END Contracted by render */
+
+
     sortObjects(canvas)
 }
 
diff --git a/frontend/src/views/newspaper_quote_middle/NewspaperQuoteMiddle.vue b/frontend/src/views/newspaper_quote_middle/NewspaperQuoteMiddle.vue
index e3e222bebbc751dc0b3eb6fef810b6b274cec2bc..f2b35c2031dba8a8ee87b669edcce66a4f269639 100644
--- a/frontend/src/views/newspaper_quote_middle/NewspaperQuoteMiddle.vue
+++ b/frontend/src/views/newspaper_quote_middle/NewspaperQuoteMiddle.vue
@@ -4,6 +4,7 @@ import { watch, ref } from 'vue'
 import COLORS from '../../colors'
 import PEOPLE from '../../people'
 import TEMPLATES from '../../templates'
+import DEFAULT_CONTRACTOR from '../../contractors'
 import { generateDefaultLogos } from '../../logos'
 import { SOURCE_IMAGES } from '../utils/newspaper_quotes'
 import { loadFonts, loadCanvasStorage, setCanvasStorage, updateAutoRedrawStorage } from '../../utils'
@@ -23,7 +24,7 @@ import AutoReloadCheckbox from '../../components/reload/AutoReloadCheckbox.vue'
 </script>
 
 <script>
-loadFonts([
+await loadFonts([
     '700 12px Glegoo',
     '12px Roboto Condensed',
     'bold 12px Roboto Condensed'
@@ -51,7 +52,8 @@ export default {
                     quotes: COLORS.yellow1,
                     baseText: COLORS.white,
                     highlightedText: COLORS.black,
-                    sourceText: COLORS.black
+                    sourceText: COLORS.black,
+                    contractedByText: COLORS.black
                 }
             },
             gray: {
@@ -63,7 +65,8 @@ export default {
                     quotes: COLORS.yellow1,
                     baseText: COLORS.black,
                     highlightedText: COLORS.black,
-                    sourceText: COLORS.white
+                    sourceText: COLORS.white,
+                    contractedByText: COLORS.gray1
                 }
             }
         }
@@ -73,6 +76,7 @@ export default {
             mainText: null,
             personName: null,
             personPosition: null,
+            contractedBy: DEFAULT_CONTRACTOR,
             logoImage: null,
             colorLabels: {
                 background: 'Pozadí',
@@ -100,6 +104,7 @@ export default {
                 mainText: this.mainText,
                 personName: this.personName,
                 personPosition: this.personPosition,
+                contractedBy: this.contractedBy,
                 logoImage: this.logoImage,
                 colors: this.colors
             }
@@ -117,6 +122,7 @@ export default {
                 vm.mainText,
                 vm.personName,
                 vm.personPosition,
+                vm.contractedBy,
                 vm.logoImage,
                 vm.colors
             ],
@@ -220,6 +226,14 @@ export default {
                     :defaultPredefinedColors="predefinedColors.base"
                     zIndex="5"
                 ></MultipleColorPicker>
+
+                <ShortTextInput
+                    name="Zadavatel a zpracovatel"
+                    v-model="contractedBy"
+                    :defaultValue="DEFAULT_CONTRACTOR"
+                    :important="false"
+                    zIndex="4"
+                />
             </template>
         </MainContainer>
     </main>
diff --git a/frontend/src/views/newspaper_quote_middle/canvas.js b/frontend/src/views/newspaper_quote_middle/canvas.js
index 63cdaacfcd50b8f690bc89f5efc8861af9ad0ab4..df78013523aa90ae3eaae8bff767a46bf48b6cd4 100644
--- a/frontend/src/views/newspaper_quote_middle/canvas.js
+++ b/frontend/src/views/newspaper_quote_middle/canvas.js
@@ -14,6 +14,8 @@ let personInfoSeparator = null
 let personPositionText = null
 let sourceText = null
 
+let contractedByTextbox = null
+
 let logoImage = null
 let quoteImage = null
 let topTearImage = null
@@ -39,6 +41,7 @@ const redraw = async (canvas, options) => {
             personNameText,
             personInfoSeparator,
             personPositionText,
+            contractedByTextbox,
             leftQuote,
             rightQuote,
             topTear,
@@ -64,6 +67,10 @@ const redraw = async (canvas, options) => {
     const bottomTextSize = Math.ceil(canvas.height * 0.03)
     const additionalContentExtraBottomPadding = Math.ceil(canvas.height * 0.03)
 
+    const contractedByTextSize = Math.ceil(canvas.height * 0.02)
+    const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9)
+    const contractedByTextSidesMargin = Math.ceil(canvas.width * 0.03)
+
     const logoWidth = Math.ceil(canvas.width * 0.2)
     const logoSideMargin = Math.ceil(canvas.width * 0.07)
 
@@ -517,6 +524,36 @@ const redraw = async (canvas, options) => {
     /* END Logo render */
 
 
+    /* BEGIN Contracted by render */
+
+    if (options.contractedBy !== null) {
+        contractedByTextbox = new fabric.Textbox(
+            options.contractedBy,
+            {
+                left: canvas.width - contractedByTextMaxWidth - contractedByTextSidesMargin,
+                top: (
+                    canvas.height
+                    - contractedByTextSidesMargin
+                    - contractedByTextSize
+                ),
+                width: contractedByTextMaxWidth,
+                fontFamily: 'Roboto Condensed',
+                fontSize: contractedByTextSize,
+                textAlign: 'right',
+                fill: options.colors.contractedByText.value,
+                selectable: false,
+                zIndex: 10
+            }
+        )
+
+        checkTextBoxHeight(contractedByTextbox, 1)
+
+        canvas.add(contractedByTextbox)
+    }
+
+    /* END Contracted by render */
+
+
     /* BEGIN Source image render */
 
     const createNewSourceImage = (
diff --git a/frontend/src/views/text_banner/TextBanner.vue b/frontend/src/views/text_banner/TextBanner.vue
index 7628449072c7c1cc57f42411740e629504f04b7a..438b43ba10590ed0e3f294c347e22f09f440a4c8 100644
--- a/frontend/src/views/text_banner/TextBanner.vue
+++ b/frontend/src/views/text_banner/TextBanner.vue
@@ -3,23 +3,30 @@ import { watch, ref } from 'vue';
 
 import COLORS from '../../colors';
 import TEMPLATES from '../../templates'
+import DEFAULT_CONTRACTOR from '../../contractors'
 import { generateDefaultLogos } from '../../logos'
 import { loadFonts, loadCanvasStorage, setCanvasStorage, updateAutoRedrawStorage } from '../../utils'
 
-import Canvas from '../../components/canvas/Canvas.vue';
-import redraw from './canvas';
+import Canvas from '../../components/canvas/Canvas.vue'
+import redraw from './canvas'
 
-import Navbar from '../../components/Navbar.vue';
-import MainContainer from '../../components/MainContainer.vue';
-import ImageInput from '../../components/inputs/ImageInput.vue';
-import LongTextInput from '../../components/inputs/text/LongTextInput.vue';
+import Navbar from '../../components/Navbar.vue'
+import MainContainer from '../../components/MainContainer.vue'
+import ImageInput from '../../components/inputs/ImageInput.vue'
+import LongTextInput from '../../components/inputs/text/LongTextInput.vue'
+import ShortTextInput from '../../components/inputs/text/ShortTextInput.vue'
 import MultipleColorPicker from '../../components/inputs/colors/MultipleColorPicker.vue'
 import ReloadButton from '../../components/reload/ReloadButton.vue'
 import AutoReloadCheckbox from '../../components/reload/AutoReloadCheckbox.vue'
 </script>
 
 <script>
-loadFonts(['12px Bebas Neue'])
+await loadFonts(
+    [
+        '12px Bebas Neue',
+        '12px Roboto Condensed'
+    ]
+)
 
 export default {
     components: {
@@ -28,6 +35,7 @@ export default {
         MainContainer,
         ImageInput,
         LongTextInput,
+        ShortTextInput,
         MultipleColorPicker
     },
     data () {
@@ -39,13 +47,15 @@ export default {
                     highlight: COLORS.yellow1,
                     arrow: COLORS.yellow1,
                     baseText: COLORS.white,
-                    highlightedText: COLORS.black
+                    highlightedText: COLORS.black,
+                    contractedByText: COLORS.gray1
                 }
             }
         }
 
         return {
             mainText: null,
+            contractedBy: DEFAULT_CONTRACTOR,
             logoImage: null,
             colorLabels: {
                 background: 'Pozadí',
@@ -67,6 +77,7 @@ export default {
         async reloadCanvasProperties () {
             const canvasProperties = {
                 mainText: this.mainText,
+                contractedBy: this.contractedBy,
                 logoImage: this.logoImage,
                 colors: this.colors
             }
@@ -81,6 +92,7 @@ export default {
         this.$watch(
             vm => [
                 vm.mainText,
+                vm.contractedBy,
                 vm.logoImage,
                 vm.colors
             ],
@@ -159,6 +171,14 @@ export default {
                     :defaultPredefinedColors="predefinedColors.base"
                     zIndex="8"
                 ></MultipleColorPicker>
+
+                <ShortTextInput
+                    name="Zadavatel a zpracovatel"
+                    v-model="contractedBy"
+                    :defaultValue="DEFAULT_CONTRACTOR"
+                    :important="false"
+                    zIndex="4"
+                />
             </template>
         </MainContainer>
     </main>
diff --git a/frontend/src/views/text_banner/canvas.js b/frontend/src/views/text_banner/canvas.js
index 65dd4ae4a44d48f8e7fc700930619898f699d416..8bc6694e0606ef0527fb516eb9b27b02dfc8c67c 100644
--- a/frontend/src/views/text_banner/canvas.js
+++ b/frontend/src/views/text_banner/canvas.js
@@ -7,12 +7,15 @@ let textBox = null
 let backgroundRect = null
 let arrow = null
 
+let contractedByTextbox = null
+
 let logoImage = null
 
 const redraw = async (canvas, options) => {
     clearObjects(
         [
             textBox,
+            contractedByTextbox,
             backgroundRect,
             arrow
         ],
@@ -29,6 +32,10 @@ const redraw = async (canvas, options) => {
     const textSize = Math.ceil(canvas.height * 0.07)
     const textLineHeight = 1
 
+    const contractedByTextSize = Math.ceil(canvas.height * 0.02)
+    const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9)
+    const contractedByTextSidesMargin = Math.ceil(canvas.width * 0.03)
+
     const arrowWidth = Math.ceil(canvas.width * 0.047)
     const arrowHeight = Math.ceil(canvas.width * 0.055)
     const arrowThickness = Math.ceil(canvas.width * 0.019)
@@ -134,6 +141,36 @@ const redraw = async (canvas, options) => {
         /* END Text render */
 
 
+        /* BEGIN Contracted by render */
+
+        if (options.contractedBy !== null) {
+            contractedByTextbox = new fabric.Textbox(
+                options.contractedBy,
+                {
+                    left: canvas.width - contractedByTextMaxWidth - contractedByTextSidesMargin,
+                    top: (
+                        canvas.height
+                        - contractedByTextSidesMargin
+                        - contractedByTextSize
+                    ),
+                    width: contractedByTextMaxWidth,
+                    fontFamily: 'Roboto Condensed',
+                    fontSize: contractedByTextSize,
+                    textAlign: 'right',
+                    fill: options.colors.contractedByText.value,
+                    selectable: false,
+                    zIndex: 10
+                }
+            )
+
+            checkTextBoxHeight(contractedByTextbox, 1)
+
+            canvas.add(contractedByTextbox)
+        }
+
+        /* END Contracted by render */
+
+
         /* BEGIN Arrow render */
 
         arrow = new fabric.Polygon(
diff --git a/frontend/src/views/twitter_banner/TwitterBanner.vue b/frontend/src/views/twitter_banner/TwitterBanner.vue
index 2ea9f412eab2b32b87e2d189cf69f68f5520d7c6..91a7f7329112d967d17d1422a756a1919081ceb7 100644
--- a/frontend/src/views/twitter_banner/TwitterBanner.vue
+++ b/frontend/src/views/twitter_banner/TwitterBanner.vue
@@ -3,6 +3,7 @@ import { watch, ref } from 'vue'
 
 import COLORS from '../../colors'
 import TEMPLATES from '../../templates'
+import DEFAULT_CONTRACTOR from '../../contractors'
 import { generateDefaultLogos } from '../../logos'
 import { loadFonts, loadCanvasStorage, setCanvasStorage, updateAutoRedrawStorage } from '../../utils'
 
@@ -14,6 +15,7 @@ import redraw from './canvas'
 import Navbar from '../../components/Navbar.vue'
 import MainContainer from '../../components/MainContainer.vue'
 import LongTextInput from '../../components/inputs/text/LongTextInput.vue'
+import ShortTextInput from '../../components/inputs/text/ShortTextInput.vue'
 import PersonInput from './PersonInput.vue'
 import ReloadButton from '../../components/reload/ReloadButton.vue'
 import AutoReloadCheckbox from '../../components/reload/AutoReloadCheckbox.vue'
@@ -28,7 +30,7 @@ import twitterLogoImage from '../../assets/template/twitter_banner/twitter.png'
 </script>
 
 <script>
-loadFonts([
+await loadFonts([
     '12px Bebas Neue',
     '12px Roboto Condensed',
     '12px Roboto'
@@ -40,6 +42,7 @@ export default {
         Navbar,
         MainContainer,
         LongTextInput,
+        ShortTextInput,
         PersonInput,
     },
     data () {
@@ -78,13 +81,15 @@ export default {
             mainText: null,
             personName: null,
             personTwitter: null,
+            contractedBy: DEFAULT_CONTRACTOR,
             logoImageSource: defaultDarkLogoImage,
             twitterLogoImageSource: twitterLogoImage,
             colors: {
                 background: COLORS.white,
                 text: COLORS.black,
                 highlight: COLORS.yellow1,
-                highlightedText: COLORS.black
+                highlightedText: COLORS.black,
+                contractedByText: COLORS.gray2
             },
             autoRedraw: false
         }
@@ -101,7 +106,8 @@ export default {
                 mainImage: this.mainImage,
                 mainText: this.mainText,
                 personName: this.personName,
-                personTwitter: this.personTwitter
+                personTwitter: this.personTwitter,
+                contractedBy: this.contractedBy
             }
 
             await this.$refs.canvas.redraw(canvasProperties)
@@ -119,7 +125,8 @@ export default {
                 vm.mainImage,
                 vm.mainText,
                 vm.personName,
-                vm.personTwitter
+                vm.personTwitter,
+                vm.contractedBy
             ],
             async (value) => {
                 if (this.autoRedraw) {
@@ -187,6 +194,14 @@ export default {
                     :important="true"
                     zIndex="9"
                 />
+
+                <ShortTextInput
+                    name="Zadavatel a zpracovatel"
+                    v-model="contractedBy"
+                    :defaultValue="DEFAULT_CONTRACTOR"
+                    :important="false"
+                    zIndex="4"
+                />
             </template>
         </MainContainer>
     </main>
diff --git a/frontend/src/views/twitter_banner/canvas.js b/frontend/src/views/twitter_banner/canvas.js
index 2156f877b38f1878abfd01ad2365058b482dfd5b..65e4b26606cda5377273341365b8555ed3888174 100644
--- a/frontend/src/views/twitter_banner/canvas.js
+++ b/frontend/src/views/twitter_banner/canvas.js
@@ -8,6 +8,8 @@ let personNameTextBox = null
 let personTwitterTextBox = null
 let mainTextBox = null
 
+let contractedByTextbox = null
+
 let mainImage = null
 let logoImage = null
 let twitterLogoImage = null
@@ -18,6 +20,7 @@ const redraw = async (canvas, options) => {
             mainTextBox,
             personNameTextBox,
             personTwitterTextBox,
+            contractedByTextbox,
         ],
         canvas
     )
@@ -42,6 +45,10 @@ const redraw = async (canvas, options) => {
     const mainTextBoxLineHeight = 1
     const mainTextBoxCharSpacing = -Math.ceil(canvas.width * 0.015)
 
+    const contractedByTextSize = Math.ceil(canvas.height * 0.02)
+    const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9)
+    const contractedByTextSidesMargin = Math.ceil(canvas.width * 0.03)
+
     const nameTextFontSize = Math.ceil(canvas.height * 0.065)
     const nameTextOffsetTop = Math.ceil(canvas.height * 0.03)
 
@@ -247,6 +254,36 @@ const redraw = async (canvas, options) => {
     /* END Logo render */
 
 
+    /* BEGIN Contracted by render */
+
+    if (options.contractedBy !== null) {
+        contractedByTextbox = new fabric.Textbox(
+            options.contractedBy,
+            {
+                left: canvas.width - contractedByTextMaxWidth - contractedByTextSidesMargin,
+                top: (
+                    canvas.height
+                    - contractedByTextSidesMargin
+                    - contractedByTextSize
+                ),
+                width: contractedByTextMaxWidth,
+                fontFamily: 'Roboto Condensed',
+                fontSize: contractedByTextSize,
+                textAlign: 'right',
+                fill: options.colors.contractedByText.value,
+                selectable: false,
+                zIndex: 10
+            }
+        )
+
+        checkTextBoxHeight(contractedByTextbox, 1)
+
+        canvas.add(contractedByTextbox)
+    }
+
+    /* END Contracted by render */
+
+
     /* BEGIN Main image render */
 
     if (
diff --git a/frontend/src/views/urgent_basic_photo_banner/UrgentBasicPhotoBanner.vue b/frontend/src/views/urgent_basic_photo_banner/UrgentBasicPhotoBanner.vue
index 2f512e8ca424ee12f734617d7b3879fe9c4bf524..aafe79f631f3e4a752544294ea9f9865599c7001 100644
--- a/frontend/src/views/urgent_basic_photo_banner/UrgentBasicPhotoBanner.vue
+++ b/frontend/src/views/urgent_basic_photo_banner/UrgentBasicPhotoBanner.vue
@@ -4,6 +4,7 @@ import { watch, ref } from 'vue'
 import COLORS from '../../colors'
 import PEOPLE from '../../people'
 import TEMPLATES from '../../templates'
+import DEFAULT_CONTRACTOR from '../../contractors'
 import { generateDefaultLogos } from '../../logos'
 import { loadFonts, loadCanvasStorage, setCanvasStorage, updateAutoRedrawStorage } from '../../utils'
 
@@ -23,7 +24,7 @@ import AutoReloadCheckbox from '../../components/reload/AutoReloadCheckbox.vue'
 </script>
 
 <script>
-loadFonts([
+await loadFonts([
     '12px Bebas Neue',
     '12px Roboto Condensed',
     'bold 12px Roboto Condensed'
@@ -50,7 +51,8 @@ export default {
                     highlight: COLORS.black,
                     arrow: COLORS.black,
                     baseText: COLORS.black,
-                    highlightedText: COLORS.white
+                    highlightedText: COLORS.white,
+                    contractedByText: COLORS.gray3
                 }
             }
         }
@@ -60,6 +62,7 @@ export default {
             mainText: null,
             personName: null,
             personPosition: null,
+            contractedBy: DEFAULT_CONTRACTOR,
             logoImage: null,
             gradientHeightMultiplier: 1,
             colorLabels: {
@@ -85,6 +88,7 @@ export default {
                 mainText: this.mainText,
                 personName: this.personName,
                 personPosition: this.personPosition,
+                contractedBy: this.contractedBy,
                 logoImage: this.logoImage,
                 gradientHeightMultiplier: this.gradientHeightMultiplier,
                 colors: this.colors
@@ -103,6 +107,7 @@ export default {
                 vm.mainText,
                 vm.personName,
                 vm.personPosition,
+                vm.contractedBy,
                 vm.logoImage,
                 vm.gradientHeightMultiplier,
                 vm.colors
@@ -214,6 +219,14 @@ export default {
                     :defaultPredefinedColors="predefinedColors.base"
                     zIndex="5"
                 ></MultipleColorPicker>
+
+                <ShortTextInput
+                    name="Zadavatel a zpracovatel"
+                    v-model="contractedBy"
+                    :defaultValue="DEFAULT_CONTRACTOR"
+                    :important="false"
+                    zIndex="4"
+                />
             </template>
         </MainContainer>
     </main>
diff --git a/frontend/src/views/urgent_text_banner/UrgentTextBanner.vue b/frontend/src/views/urgent_text_banner/UrgentTextBanner.vue
index 6c02fe09af7e6d8ece47848d48ca071597f50193..8aa62fd70954a5b65e246ccd8eed282d363838d2 100644
--- a/frontend/src/views/urgent_text_banner/UrgentTextBanner.vue
+++ b/frontend/src/views/urgent_text_banner/UrgentTextBanner.vue
@@ -3,23 +3,25 @@ import { watch, ref } from 'vue';
 
 import COLORS from '../../colors';
 import TEMPLATES from '../../templates'
+import DEFAULT_CONTRACTOR from '../../contractors'
 import { LOGOS } from '../../logos'
 import { loadFonts, loadCanvasStorage, setCanvasStorage, updateAutoRedrawStorage } from '../../utils'
 
-import Canvas from '../../components/canvas/Canvas.vue';
-import redraw from './canvas';
+import Canvas from '../../components/canvas/Canvas.vue'
+import redraw from './canvas'
 
-import Navbar from '../../components/Navbar.vue';
-import MainContainer from '../../components/MainContainer.vue';
-import ImageInput from '../../components/inputs/ImageInput.vue';
-import LongTextInput from '../../components/inputs/text/LongTextInput.vue';
+import Navbar from '../../components/Navbar.vue'
+import MainContainer from '../../components/MainContainer.vue'
+import ImageInput from '../../components/inputs/ImageInput.vue'
+import LongTextInput from '../../components/inputs/text/LongTextInput.vue'
+import ShortTextInput from '../../components/inputs/text/ShortTextInput.vue'
 import MultipleColorPicker from '../../components/inputs/colors/MultipleColorPicker.vue'
 import ReloadButton from '../../components/reload/ReloadButton.vue'
 import AutoReloadCheckbox from '../../components/reload/AutoReloadCheckbox.vue'
 </script>
 
 <script>
-loadFonts([
+await loadFonts([
     '12px Bebas Neue',
     '12px Roboto Condensed',
     'bold 12px Roboto Condensed'
@@ -32,6 +34,7 @@ export default {
         MainContainer,
         ImageInput,
         LongTextInput,
+        ShortTextInput,
         MultipleColorPicker
     },
     data () {
@@ -42,12 +45,14 @@ export default {
                     background: COLORS.black,
                     cross: COLORS.yellow1,
                     text: COLORS.black,
+                    contractedByText: COLORS.gray2
                 }
             }
         }
 
         return {
             mainText: null,
+            contractedBy: DEFAULT_CONTRACTOR,
             logoImage: null,
             colorLabels: {
                 background: 'Pozadí',
@@ -73,6 +78,7 @@ export default {
         async reloadCanvasProperties () {
             const canvasProperties = {
                 mainText: this.mainText,
+                contractedBy: this.contractedBy,
                 logoImage: this.logoImage,
                 colors: this.colors
             }
@@ -87,6 +93,7 @@ export default {
         this.$watch(
             vm => [
                 vm.mainText,
+                vm.contractedBy,
                 vm.logoImage,
                 vm.colors
             ],
@@ -165,6 +172,14 @@ export default {
                     :defaultPredefinedColors="predefinedColors.base"
                     zIndex="8"
                 ></MultipleColorPicker>
+
+                <ShortTextInput
+                    name="Zadavatel a zpracovatel"
+                    v-model="contractedBy"
+                    :defaultValue="DEFAULT_CONTRACTOR"
+                    :important="false"
+                    zIndex="4"
+                />
             </template>
         </MainContainer>
     </main>
diff --git a/frontend/src/views/urgent_text_banner/canvas.js b/frontend/src/views/urgent_text_banner/canvas.js
index 300d2173bf830479ccfdf4724636029ab9ecb9df..ad8b1f5235edd921c223644da90ee3f7af5cb8b0 100644
--- a/frontend/src/views/urgent_text_banner/canvas.js
+++ b/frontend/src/views/urgent_text_banner/canvas.js
@@ -2,13 +2,18 @@ import { fabric } from 'fabric'
 import { clearObjects, sortObjects, transformTextLineBreaks, checkTextBoxHeight } from '../../components/canvas/utils'
 import backgroundURL from '../../assets/template/urgent_text_banner/background.png'
 
+let contractedByTextbox = null
+
 let textBox = null
 let backgroundImage = null
 let logoImage = null
 
 const redraw = async (canvas, options) => {
     clearObjects(
-        [textBox],
+        [
+            textBox,
+            contractedByTextbox
+        ],
         canvas
     )
 
@@ -16,6 +21,10 @@ const redraw = async (canvas, options) => {
     const logoSideMargin = Math.ceil(canvas.width * 0.07)
     const logoTopMargin = Math.ceil(canvas.height * 0.155)
 
+    const contractedByTextSize = Math.ceil(canvas.height * 0.02)
+    const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9)
+    const contractedByTextSidesMargin = Math.ceil(canvas.width * 0.03)
+
     const textBoxWidth = Math.ceil(canvas.width * 0.65)
     const textSize = Math.ceil(canvas.height * 0.0725)
     const textLineHeight = 0.95
@@ -87,6 +96,37 @@ const redraw = async (canvas, options) => {
 
     /* END Logo render */
 
+
+    /* BEGIN Contracted by render */
+
+    if (options.contractedBy !== null) {
+        contractedByTextbox = new fabric.Textbox(
+            options.contractedBy,
+            {
+                left: canvas.width - contractedByTextMaxWidth - contractedByTextSidesMargin,
+                top: (
+                    canvas.height
+                    - contractedByTextSidesMargin
+                    - contractedByTextSize
+                ),
+                width: contractedByTextMaxWidth,
+                fontFamily: 'Roboto Condensed',
+                fontSize: contractedByTextSize,
+                textAlign: 'right',
+                fill: options.colors.contractedByText.value,
+                selectable: false,
+                zIndex: 10
+            }
+        )
+
+        checkTextBoxHeight(contractedByTextbox, 1)
+
+        canvas.add(contractedByTextbox)
+    }
+
+    /* END Contracted by render */
+
+
     /* BEGIN Text render */
 
     if (options.mainText !== null) {
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000000000000000000000000000000000000..808442b68fd93a93c436975ccbc246b62f7dca76
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,10 @@
+{
+  "name": "graphics-generator",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "graphics-generator"
+    }
+  }
+}
diff --git a/server/server/templates/index.html b/server/server/templates/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..c9ec12e6950ec1ad954306cf70cc852de08fc637
--- /dev/null
+++ b/server/server/templates/index.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+    <link rel="icon" href="/static/favicon.ico">
+    <link rel="stylesheet" href="https://styleguide.pirati.cz/2.12.x/css/styles.css">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Generátor grafiky</title>
+    <script type="module" crossorigin src="/static/index-f03fa345.js"></script>
+    <link rel="stylesheet" href="/static/index-ba72a822.css">
+  </head>
+  <body>
+    <div id="app"></div>
+
+  </body>
+</html>