Added edit calendar events and fixed timezone
This commit is contained in:
parent
85ea87201d
commit
ac995b1b83
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -3,5 +3,6 @@
|
|||||||
"phpstan.configFile": "backend/phpstan.dist.neon",
|
"phpstan.configFile": "backend/phpstan.dist.neon",
|
||||||
"phpstan.checkValidity": true,
|
"phpstan.checkValidity": true,
|
||||||
"phpstan.showTypeOnHover": false,
|
"phpstan.showTypeOnHover": false,
|
||||||
"phpstan.showProgress": true
|
"phpstan.showProgress": true,
|
||||||
|
"php.version": "8.4"
|
||||||
}
|
}
|
||||||
@ -26,5 +26,5 @@ APP_SECRET=71bf50bfb778d456b3a376ff60d5dcd8
|
|||||||
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
|
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
|
||||||
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4"
|
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4"
|
||||||
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
|
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
|
||||||
DATABASE_URL="postgresql://postgres:postgres@calendi-postgres:5432/postgres?serverVersion=16&charset=utf8"
|
DATABASE_URL="postgresql://postgres:postgres@calendi-postgres.test:5432/postgres?serverVersion=16&charset=utf8"
|
||||||
###< doctrine/doctrine-bundle ###
|
###< doctrine/doctrine-bundle ###
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"minimum-stability": "stable",
|
"minimum-stability": "stable",
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.1",
|
"php": ">=8.4",
|
||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
"ext-iconv": "*",
|
"ext-iconv": "*",
|
||||||
"doctrine/annotations": "^2.0",
|
"doctrine/annotations": "^2.0",
|
||||||
@ -20,6 +20,7 @@
|
|||||||
"symfony/flex": "^2",
|
"symfony/flex": "^2",
|
||||||
"symfony/framework-bundle": "6.4.*",
|
"symfony/framework-bundle": "6.4.*",
|
||||||
"symfony/http-client": "6.4.*",
|
"symfony/http-client": "6.4.*",
|
||||||
|
"symfony/monolog-bundle": "^3.10",
|
||||||
"symfony/property-access": "6.4.*",
|
"symfony/property-access": "6.4.*",
|
||||||
"symfony/property-info": "6.4.*",
|
"symfony/property-info": "6.4.*",
|
||||||
"symfony/runtime": "6.4.*",
|
"symfony/runtime": "6.4.*",
|
||||||
@ -83,6 +84,8 @@
|
|||||||
"doctrine/doctrine-fixtures-bundle": "^4.1",
|
"doctrine/doctrine-fixtures-bundle": "^4.1",
|
||||||
"phpstan/phpstan": "^2.1",
|
"phpstan/phpstan": "^2.1",
|
||||||
"phpstan/phpstan-symfony": "^2.0",
|
"phpstan/phpstan-symfony": "^2.0",
|
||||||
"symfony/maker-bundle": "^1.62"
|
"symfony/maker-bundle": "^1.62",
|
||||||
|
"symfony/stopwatch": "6.4.*",
|
||||||
|
"symfony/web-profiler-bundle": "6.4.*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
349
backend/composer.lock
generated
349
backend/composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "f41287711c3c1d476ebbca47f5b529b5",
|
"content-hash": "7ec99e86c547c32beef698aea0e9e346",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "doctrine/annotations",
|
"name": "doctrine/annotations",
|
||||||
@ -1202,6 +1202,109 @@
|
|||||||
},
|
},
|
||||||
"time": "2025-01-24T11:45:48+00:00"
|
"time": "2025-01-24T11:45:48+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "monolog/monolog",
|
||||||
|
"version": "3.9.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Seldaek/monolog.git",
|
||||||
|
"reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6",
|
||||||
|
"reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.1",
|
||||||
|
"psr/log": "^2.0 || ^3.0"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"psr/log-implementation": "3.0.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"aws/aws-sdk-php": "^3.0",
|
||||||
|
"doctrine/couchdb": "~1.0@dev",
|
||||||
|
"elasticsearch/elasticsearch": "^7 || ^8",
|
||||||
|
"ext-json": "*",
|
||||||
|
"graylog2/gelf-php": "^1.4.2 || ^2.0",
|
||||||
|
"guzzlehttp/guzzle": "^7.4.5",
|
||||||
|
"guzzlehttp/psr7": "^2.2",
|
||||||
|
"mongodb/mongodb": "^1.8",
|
||||||
|
"php-amqplib/php-amqplib": "~2.4 || ^3",
|
||||||
|
"php-console/php-console": "^3.1.8",
|
||||||
|
"phpstan/phpstan": "^2",
|
||||||
|
"phpstan/phpstan-deprecation-rules": "^2",
|
||||||
|
"phpstan/phpstan-strict-rules": "^2",
|
||||||
|
"phpunit/phpunit": "^10.5.17 || ^11.0.7",
|
||||||
|
"predis/predis": "^1.1 || ^2",
|
||||||
|
"rollbar/rollbar": "^4.0",
|
||||||
|
"ruflin/elastica": "^7 || ^8",
|
||||||
|
"symfony/mailer": "^5.4 || ^6",
|
||||||
|
"symfony/mime": "^5.4 || ^6"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
|
||||||
|
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
|
||||||
|
"elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
|
||||||
|
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
|
||||||
|
"ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
|
||||||
|
"ext-mbstring": "Allow to work properly with unicode symbols",
|
||||||
|
"ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
|
||||||
|
"ext-openssl": "Required to send log messages using SSL",
|
||||||
|
"ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
|
||||||
|
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
|
||||||
|
"mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
|
||||||
|
"php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
|
||||||
|
"rollbar/rollbar": "Allow sending log messages to Rollbar",
|
||||||
|
"ruflin/elastica": "Allow sending log messages to an Elastic Search server"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "3.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Monolog\\": "src/Monolog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jordi Boggiano",
|
||||||
|
"email": "j.boggiano@seld.be",
|
||||||
|
"homepage": "https://seld.be"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Sends your logs to files, sockets, inboxes, databases and various web services",
|
||||||
|
"homepage": "https://github.com/Seldaek/monolog",
|
||||||
|
"keywords": [
|
||||||
|
"log",
|
||||||
|
"logging",
|
||||||
|
"psr-3"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/Seldaek/monolog/issues",
|
||||||
|
"source": "https://github.com/Seldaek/monolog/tree/3.9.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/Seldaek",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-03-24T10:02:05+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "nelmio/api-doc-bundle",
|
"name": "nelmio/api-doc-bundle",
|
||||||
"version": "v5.0.1",
|
"version": "v5.0.1",
|
||||||
@ -3485,6 +3588,166 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-03-28T13:27:10+00:00"
|
"time": "2025-03-28T13:27:10+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/monolog-bridge",
|
||||||
|
"version": "v6.4.13",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/monolog-bridge.git",
|
||||||
|
"reference": "9d14621e59f22c2b6d030d92d37ffe5ae1e60452"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/9d14621e59f22c2b6d030d92d37ffe5ae1e60452",
|
||||||
|
"reference": "9d14621e59f22c2b6d030d92d37ffe5ae1e60452",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"monolog/monolog": "^1.25.1|^2|^3",
|
||||||
|
"php": ">=8.1",
|
||||||
|
"symfony/deprecation-contracts": "^2.5|^3",
|
||||||
|
"symfony/http-kernel": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/service-contracts": "^2.5|^3"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"symfony/console": "<5.4",
|
||||||
|
"symfony/http-foundation": "<5.4",
|
||||||
|
"symfony/security-core": "<5.4"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/console": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/http-client": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/mailer": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/messenger": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/mime": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/security-core": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/var-dumper": "^5.4|^6.0|^7.0"
|
||||||
|
},
|
||||||
|
"type": "symfony-bridge",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Bridge\\Monolog\\": ""
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": [
|
||||||
|
"/Tests/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Potencier",
|
||||||
|
"email": "fabien@symfony.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Provides integration for Monolog with various Symfony components",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/monolog-bridge/tree/v6.4.13"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-10-14T08:49:08+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/monolog-bundle",
|
||||||
|
"version": "v3.10.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/monolog-bundle.git",
|
||||||
|
"reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181",
|
||||||
|
"reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"monolog/monolog": "^1.25.1 || ^2.0 || ^3.0",
|
||||||
|
"php": ">=7.2.5",
|
||||||
|
"symfony/config": "^5.4 || ^6.0 || ^7.0",
|
||||||
|
"symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0",
|
||||||
|
"symfony/http-kernel": "^5.4 || ^6.0 || ^7.0",
|
||||||
|
"symfony/monolog-bridge": "^5.4 || ^6.0 || ^7.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/console": "^5.4 || ^6.0 || ^7.0",
|
||||||
|
"symfony/phpunit-bridge": "^6.3 || ^7.0",
|
||||||
|
"symfony/yaml": "^5.4 || ^6.0 || ^7.0"
|
||||||
|
},
|
||||||
|
"type": "symfony-bundle",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Bundle\\MonologBundle\\": ""
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": [
|
||||||
|
"/Tests/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Potencier",
|
||||||
|
"email": "fabien@symfony.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony MonologBundle",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"log",
|
||||||
|
"logging"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/symfony/monolog-bundle/issues",
|
||||||
|
"source": "https://github.com/symfony/monolog-bundle/tree/v3.10.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2023-11-06T17:08:13+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/options-resolver",
|
"name": "symfony/options-resolver",
|
||||||
"version": "v6.4.16",
|
"version": "v6.4.16",
|
||||||
@ -6027,6 +6290,88 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-03-10T17:11:00+00:00"
|
"time": "2025-03-10T17:11:00+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/web-profiler-bundle",
|
||||||
|
"version": "v6.4.19",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/web-profiler-bundle.git",
|
||||||
|
"reference": "7d1026a8e950d416cb5148ae88ac23db5d264839"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/7d1026a8e950d416cb5148ae88ac23db5d264839",
|
||||||
|
"reference": "7d1026a8e950d416cb5148ae88ac23db5d264839",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.1",
|
||||||
|
"symfony/config": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/framework-bundle": "^6.4|^7.0",
|
||||||
|
"symfony/http-kernel": "^6.4|^7.0",
|
||||||
|
"symfony/routing": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/twig-bundle": "^5.4|^6.0",
|
||||||
|
"twig/twig": "^2.13|^3.0.4"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"symfony/form": "<5.4",
|
||||||
|
"symfony/mailer": "<5.4",
|
||||||
|
"symfony/messenger": "<5.4",
|
||||||
|
"symfony/twig-bundle": ">=7.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/browser-kit": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/console": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/css-selector": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/stopwatch": "^5.4|^6.0|^7.0"
|
||||||
|
},
|
||||||
|
"type": "symfony-bundle",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Bundle\\WebProfilerBundle\\": ""
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": [
|
||||||
|
"/Tests/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Potencier",
|
||||||
|
"email": "fabien@symfony.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Provides a development tool that gives detailed information about the execution of any request",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"dev"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.19"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-02-14T12:21:59+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
@ -6035,7 +6380,7 @@
|
|||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": ">=8.1",
|
"php": ">=8.4",
|
||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
"ext-iconv": "*"
|
"ext-iconv": "*"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -8,4 +8,6 @@ return [
|
|||||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||||
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
|
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
|
||||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||||
|
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||||
|
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
|||||||
62
backend/config/packages/monolog.yaml
Normal file
62
backend/config/packages/monolog.yaml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
monolog:
|
||||||
|
channels:
|
||||||
|
- deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists
|
||||||
|
|
||||||
|
when@dev:
|
||||||
|
monolog:
|
||||||
|
handlers:
|
||||||
|
main:
|
||||||
|
type: stream
|
||||||
|
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||||
|
level: debug
|
||||||
|
channels: ["!event"]
|
||||||
|
# uncomment to get logging in your browser
|
||||||
|
# you may have to allow bigger header sizes in your Web server configuration
|
||||||
|
#firephp:
|
||||||
|
# type: firephp
|
||||||
|
# level: info
|
||||||
|
#chromephp:
|
||||||
|
# type: chromephp
|
||||||
|
# level: info
|
||||||
|
console:
|
||||||
|
type: console
|
||||||
|
process_psr_3_messages: false
|
||||||
|
channels: ["!event", "!doctrine", "!console"]
|
||||||
|
|
||||||
|
when@test:
|
||||||
|
monolog:
|
||||||
|
handlers:
|
||||||
|
main:
|
||||||
|
type: fingers_crossed
|
||||||
|
action_level: error
|
||||||
|
handler: nested
|
||||||
|
excluded_http_codes: [404, 405]
|
||||||
|
channels: ["!event"]
|
||||||
|
nested:
|
||||||
|
type: stream
|
||||||
|
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||||
|
level: debug
|
||||||
|
|
||||||
|
when@prod:
|
||||||
|
monolog:
|
||||||
|
handlers:
|
||||||
|
main:
|
||||||
|
type: fingers_crossed
|
||||||
|
action_level: error
|
||||||
|
handler: nested
|
||||||
|
excluded_http_codes: [404, 405]
|
||||||
|
buffer_size: 50 # How many messages should be saved? Prevent memory leaks
|
||||||
|
nested:
|
||||||
|
type: stream
|
||||||
|
path: php://stderr
|
||||||
|
level: debug
|
||||||
|
formatter: monolog.formatter.json
|
||||||
|
console:
|
||||||
|
type: console
|
||||||
|
process_psr_3_messages: false
|
||||||
|
channels: ["!event", "!doctrine"]
|
||||||
|
deprecation:
|
||||||
|
type: stream
|
||||||
|
channels: [deprecation]
|
||||||
|
path: php://stderr
|
||||||
|
formatter: monolog.formatter.json
|
||||||
11
backend/config/packages/web_profiler.yaml
Normal file
11
backend/config/packages/web_profiler.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
when@dev:
|
||||||
|
web_profiler:
|
||||||
|
toolbar: true
|
||||||
|
|
||||||
|
framework:
|
||||||
|
profiler:
|
||||||
|
collect_serializer_data: true
|
||||||
|
|
||||||
|
when@test:
|
||||||
|
framework:
|
||||||
|
profiler: { collect: false }
|
||||||
8
backend/config/routes/web_profiler.yaml
Normal file
8
backend/config/routes/web_profiler.yaml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
when@dev:
|
||||||
|
web_profiler_wdt:
|
||||||
|
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
|
||||||
|
prefix: /_wdt
|
||||||
|
|
||||||
|
web_profiler_profiler:
|
||||||
|
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
|
||||||
|
prefix: /_profiler
|
||||||
@ -4,6 +4,7 @@
|
|||||||
# Put parameters here that don't need to change on each machine where the app is deployed
|
# Put parameters here that don't need to change on each machine where the app is deployed
|
||||||
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
|
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
|
||||||
parameters:
|
parameters:
|
||||||
|
app.timezone: 'Europe/Berlin'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# default configuration for services in *this* file
|
# default configuration for services in *this* file
|
||||||
|
|||||||
@ -15,6 +15,7 @@ use Nelmio\ApiDocBundle\Attribute\Model;
|
|||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
|
|
||||||
#[Route('/api/events', name: 'api_events_')]
|
#[Route('/api/events', name: 'api_events_')]
|
||||||
|
#[OA\Tag(name: 'Events')]
|
||||||
class GetEventsController extends AbstractController
|
class GetEventsController extends AbstractController
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
@ -23,7 +24,6 @@ class GetEventsController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[Route('', name: 'list', methods: ['GET'])]
|
#[Route('', name: 'list', methods: ['GET'])]
|
||||||
#[OA\Tag(name: 'Events')]
|
|
||||||
#[OA\Response(
|
#[OA\Response(
|
||||||
response: 200,
|
response: 200,
|
||||||
description: 'Returns list of events',
|
description: 'Returns list of events',
|
||||||
@ -39,7 +39,6 @@ class GetEventsController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[Route('/{id}', name: 'get', methods: ['GET'])]
|
#[Route('/{id}', name: 'get', methods: ['GET'])]
|
||||||
#[OA\Tag(name: 'Events')]
|
|
||||||
#[OA\Parameter(
|
#[OA\Parameter(
|
||||||
name: 'id',
|
name: 'id',
|
||||||
description: 'Event ID',
|
description: 'Event ID',
|
||||||
|
|||||||
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Application\Controller\Event;
|
||||||
|
|
||||||
|
use App\Application\DTO\PersistEventDTO;
|
||||||
|
use App\Domain\Event\PersistEventHandler;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
use OpenApi\Attributes as OA;
|
||||||
|
|
||||||
|
#[Route('/api/events', name: 'api_events_persist', methods: ['POST'])]
|
||||||
|
#[OA\Tag(name: 'Events')]
|
||||||
|
class PersistEventController extends AbstractController
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly PersistEventHandler $persistEventHandler,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('', name: 'persist', methods: ['POST'])]
|
||||||
|
public function persist(#[MapRequestPayload] PersistEventDTO $dto): JsonResponse
|
||||||
|
{
|
||||||
|
$this->persistEventHandler->handle($dto->toDomain());
|
||||||
|
return $this->json(['message' => 'Event persisted']);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/{id}', name: 'update', methods: ['PUT'])]
|
||||||
|
public function update(#[MapRequestPayload] PersistEventDTO $dto, string $id): JsonResponse
|
||||||
|
{
|
||||||
|
$this->persistEventHandler->handle($dto->toDomain()->withId($id));
|
||||||
|
return $this->json(['message' => 'Event updated']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,36 +5,48 @@ declare(strict_types=1);
|
|||||||
namespace App\Application\DTO;
|
namespace App\Application\DTO;
|
||||||
|
|
||||||
use App\Domain\Model\EventDraft;
|
use App\Domain\Model\EventDraft;
|
||||||
|
use DateTimeInterface;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
|
|
||||||
#[OA\Schema]
|
#[OA\Schema]
|
||||||
final readonly class EventDraftDTO
|
final readonly class EventDraftDTO
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
#[OA\Property(type: 'string')]
|
#[OA\Property(type: 'string', nullable: true)]
|
||||||
public string $id,
|
public ?string $title = null,
|
||||||
#[OA\Property(type: 'string')]
|
#[OA\Property(type: 'string', nullable: true)]
|
||||||
public string $title,
|
public ?string $description = null,
|
||||||
#[OA\Property(type: 'string')]
|
#[OA\Property(type: 'string', nullable: true)]
|
||||||
public string $description,
|
public ?string $location = null,
|
||||||
#[OA\Property(type: 'string')]
|
#[OA\Property(type: 'datetime', nullable: true)]
|
||||||
public ?string $start,
|
public ?DateTimeInterface $start = null,
|
||||||
#[OA\Property(type: 'string')]
|
#[OA\Property(type: 'datetime', nullable: true)]
|
||||||
public ?string $end,
|
public ?DateTimeInterface $end = null,
|
||||||
#[OA\Property(type: 'boolean')]
|
#[OA\Property(type: 'boolean', nullable: true)]
|
||||||
public bool $allDay
|
public ?bool $allDay = null,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function toDomain(): EventDraft
|
||||||
|
{
|
||||||
|
return new EventDraft(
|
||||||
|
$this->title,
|
||||||
|
$this->description,
|
||||||
|
$this->location,
|
||||||
|
$this->start,
|
||||||
|
$this->end,
|
||||||
|
$this->allDay,
|
||||||
|
);
|
||||||
|
}
|
||||||
public static function fromDraft(EventDraft $draft): self
|
public static function fromDraft(EventDraft $draft): self
|
||||||
{
|
{
|
||||||
return new self(
|
return new self(
|
||||||
$draft->title(),
|
$draft->title,
|
||||||
$draft->description(),
|
$draft->description,
|
||||||
$draft->location(),
|
$draft->location,
|
||||||
$draft->start()?->format('Y-m-d H:i:s'),
|
$draft->start,
|
||||||
$draft->end()?->format('Y-m-d H:i:s'),
|
$draft->end,
|
||||||
$draft->allDay()
|
$draft->allDay,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
24
backend/src/Application/DTO/PersistEventDTO.php
Normal file
24
backend/src/Application/DTO/PersistEventDTO.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Application\DTO;
|
||||||
|
|
||||||
|
use App\Domain\Event\PersistEvent;
|
||||||
|
use OpenApi\Attributes as OA;
|
||||||
|
use App\Application\DTO\EventDraftDTO;
|
||||||
|
|
||||||
|
#[OA\Schema]
|
||||||
|
final readonly class PersistEventDTO
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
#[OA\Property]
|
||||||
|
public readonly EventDraftDTO $draft,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toDomain(): PersistEvent
|
||||||
|
{
|
||||||
|
return new PersistEvent(
|
||||||
|
$this->draft->toDomain(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,12 +6,14 @@ use App\Domain\Chat\ChatProviderInterface;
|
|||||||
use App\Domain\Chat\ChatSession;
|
use App\Domain\Chat\ChatSession;
|
||||||
use App\Domain\Model\EventDraft;
|
use App\Domain\Model\EventDraft;
|
||||||
use App\Domain\User\UserContextProvider;
|
use App\Domain\User\UserContextProvider;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
class GenerateDraftHandler
|
class GenerateDraftHandler
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ChatProviderInterface $chatProvider,
|
private readonly ChatProviderInterface $chatProvider,
|
||||||
private readonly UserContextProvider $userContextProvider,
|
private readonly UserContextProvider $userContextProvider,
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,14 +21,20 @@ class GenerateDraftHandler
|
|||||||
{
|
{
|
||||||
$userContext = $this->userContextProvider->getUserContext();
|
$userContext = $this->userContextProvider->getUserContext();
|
||||||
$chat = new ChatSession($this->chatProvider);
|
$chat = new ChatSession($this->chatProvider);
|
||||||
$chat->system(<<<PROMPT
|
|
||||||
You are a helpful assistant that generates event drafts based on user input.
|
$systemPrompt = <<<PROMPT
|
||||||
|
You are a helpful assistant that generates calendar event drafts based on user input.
|
||||||
The user input can be anything from a very detailed description to a simple sentence or just a snippet from another source (Chat, email, etc.).
|
The user input can be anything from a very detailed description to a simple sentence or just a snippet from another source (Chat, email, etc.).
|
||||||
You should always generate a draft even if the user input is not very detailed.
|
You should always generate a draft even if the user input is not very detailed.
|
||||||
Go Step by Step by step:
|
Go Step by Step by step:
|
||||||
- First, analyze the user input and analyze the context of the user input.
|
- First, analyze the user input and asking yourself what the user wants to achieve:
|
||||||
|
a. What is the title of the event?
|
||||||
|
b. What is the description of the event?
|
||||||
|
c. What is the location of the event?
|
||||||
|
d. What is the start datetime of the event?
|
||||||
|
e. What is the end datetime of the event?
|
||||||
|
f. Is the event all day?
|
||||||
- Then, generate a draft of the event.
|
- Then, generate a draft of the event.
|
||||||
- Finally, return the draft in the following format:
|
|
||||||
|
|
||||||
The event draft should be in the following format:
|
The event draft should be in the following format:
|
||||||
```json
|
```json
|
||||||
@ -42,9 +50,14 @@ class GenerateDraftHandler
|
|||||||
|
|
||||||
This is the current context:
|
This is the current context:
|
||||||
- Time: {$userContext->now()->format('Y-m-d H:i:s')}
|
- Time: {$userContext->now()->format('Y-m-d H:i:s')}
|
||||||
|
- Timezone: {$userContext->now()->getTimezone()->getName()}
|
||||||
|
|
||||||
You will only respond with the JSON object in ```json``` tags, nothing else.
|
You will only respond with the JSON object in ```json``` tags, nothing else.
|
||||||
PROMPT);
|
PROMPT;
|
||||||
|
|
||||||
|
$chat->system($systemPrompt);
|
||||||
|
|
||||||
|
$this->logger->info('Generating draft for input: ' . $generateDraft->input() . " System prompt: " . $systemPrompt);
|
||||||
|
|
||||||
$chat->user($generateDraft->input());
|
$chat->user($generateDraft->input());
|
||||||
$chat->commit(reasoning: false);
|
$chat->commit(reasoning: false);
|
||||||
|
|||||||
19
backend/src/Domain/Event/PersistEvent.php
Normal file
19
backend/src/Domain/Event/PersistEvent.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domain\Event;
|
||||||
|
|
||||||
|
use App\Domain\Model\EventDraft;
|
||||||
|
|
||||||
|
class PersistEvent
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public readonly EventDraft $draft,
|
||||||
|
public readonly ?string $id = null,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withId(string $id): self
|
||||||
|
{
|
||||||
|
return new self($this->draft, $id);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
backend/src/Domain/Event/PersistEventHandler.php
Normal file
28
backend/src/Domain/Event/PersistEventHandler.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domain\Event;
|
||||||
|
|
||||||
|
use App\Domain\Event\PersistEvent;
|
||||||
|
use App\Domain\Model\Persisted\PersistedEvent;
|
||||||
|
use App\Infrastructure\Repository\EventRepository;
|
||||||
|
|
||||||
|
class PersistEventHandler
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly EventRepository $eventRepository,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(PersistEvent $event): void
|
||||||
|
{
|
||||||
|
$persistedEvent = new PersistedEvent();
|
||||||
|
if ($event->id !== null) {
|
||||||
|
$persistedEvent = $this->eventRepository->find($event->id);
|
||||||
|
if (!$persistedEvent) {
|
||||||
|
throw new \Exception('Event not found');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->eventRepository->save($event->draft->mergeIntoPersisted($persistedEvent));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,47 +2,49 @@
|
|||||||
|
|
||||||
namespace App\Domain\Model;
|
namespace App\Domain\Model;
|
||||||
|
|
||||||
|
use App\Domain\Model\Persisted\PersistedEvent;
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
|
|
||||||
class EventDraft
|
class EventDraft
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly string $title,
|
public readonly ?string $title,
|
||||||
private readonly string $description,
|
public readonly ?string $description,
|
||||||
private readonly string $location,
|
public readonly ?string $location,
|
||||||
private readonly ?DateTimeInterface $start,
|
public readonly ?DateTimeInterface $start,
|
||||||
private readonly ?DateTimeInterface $end,
|
public readonly ?DateTimeInterface $end,
|
||||||
private readonly bool $allDay,
|
public readonly ?bool $allDay,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function title(): string
|
public function mergeIntoPersisted(PersistedEvent $persistedEvent = new PersistedEvent()): PersistedEvent
|
||||||
{
|
{
|
||||||
return $this->title;
|
if (!$this->start instanceof \DateTimeImmutable) {
|
||||||
}
|
throw new \Exception('Start date must be a DateTimeImmutable');
|
||||||
|
}
|
||||||
|
|
||||||
public function description(): string
|
if (!$this->end instanceof \DateTimeImmutable) {
|
||||||
{
|
throw new \Exception('End date must be a DateTimeImmutable');
|
||||||
return $this->description;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function location(): string
|
if ($this->allDay === null) {
|
||||||
{
|
throw new \Exception('All day must be a boolean');
|
||||||
return $this->location;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function start(): ?DateTimeInterface
|
if ($this->title === null) {
|
||||||
{
|
throw new \Exception('Title must be a string');
|
||||||
return $this->start;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function end(): ?DateTimeInterface
|
if ($this->description === null) {
|
||||||
{
|
throw new \Exception('Description must be a string');
|
||||||
return $this->end;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function allDay(): bool
|
return $persistedEvent
|
||||||
{
|
->setTitle($this->title)
|
||||||
return $this->allDay;
|
->setDescription($this->description)
|
||||||
|
->setLocation($this->location)
|
||||||
|
->setStart($this->start)
|
||||||
|
->setEnd($this->end)
|
||||||
|
->setAllDay($this->allDay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -23,6 +23,9 @@ class PersistedEvent
|
|||||||
#[ORM\Column(type: 'text', nullable: true)]
|
#[ORM\Column(type: 'text', nullable: true)]
|
||||||
private ?string $description = null;
|
private ?string $description = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||||
|
private ?string $location = null;
|
||||||
|
|
||||||
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'event_from')]
|
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'event_from')]
|
||||||
#[Assert\NotNull]
|
#[Assert\NotNull]
|
||||||
private \DateTimeImmutable $from;
|
private \DateTimeImmutable $from;
|
||||||
@ -128,4 +131,16 @@ class PersistedEvent
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getLocation(): ?string
|
||||||
|
{
|
||||||
|
return $this->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLocation(?string $location): self
|
||||||
|
{
|
||||||
|
$this->location = $location;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Infrastructure\EventSubscriber;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||||
|
use Symfony\Component\HttpKernel\KernelEvents;
|
||||||
|
|
||||||
|
final class TimezoneSubscriber implements EventSubscriberInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly ParameterBagInterface $params,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getSubscribedEvents(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
KernelEvents::REQUEST => ['setTimezone', 100],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTimezone(RequestEvent $event): void
|
||||||
|
{
|
||||||
|
if (!$event->isMainRequest()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timezone = $this->params->get('app.timezone');
|
||||||
|
date_default_timezone_set($timezone);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,6 +16,12 @@ class EventRepository extends ServiceEntityRepository
|
|||||||
parent::__construct($registry, PersistedEvent::class);
|
parent::__construct($registry, PersistedEvent::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function save(PersistedEvent $event): void
|
||||||
|
{
|
||||||
|
$this->getEntityManager()->persist($event);
|
||||||
|
$this->getEntityManager()->flush();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<PersistedEvent>
|
* @return array<PersistedEvent>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -121,6 +121,18 @@
|
|||||||
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
|
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"symfony/monolog-bundle": {
|
||||||
|
"version": "3.10",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "3.7",
|
||||||
|
"ref": "aff23899c4440dd995907613c1dd709b6f59503f"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/monolog.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
"symfony/routing": {
|
"symfony/routing": {
|
||||||
"version": "6.4",
|
"version": "6.4",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
@ -170,5 +182,18 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"config/packages/validator.yaml"
|
"config/packages/validator.yaml"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"symfony/web-profiler-bundle": {
|
||||||
|
"version": "6.4",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "6.1",
|
||||||
|
"ref": "8b51135b84f4266e3b4c8a6dc23c9d1e32e543b7"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/web_profiler.yaml",
|
||||||
|
"config/routes/web_profiler.yaml"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: app:latest
|
image: app:latest
|
||||||
|
hostname: calendi.test
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: docker/Dockerfile
|
dockerfile: docker/Dockerfile
|
||||||
@ -17,7 +18,7 @@ services:
|
|||||||
- proxy
|
- proxy
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
hostname: calendi-postgres
|
hostname: calendi-postgres.test
|
||||||
image: postgres:15
|
image: postgres:15
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
@ -29,6 +30,11 @@ services:
|
|||||||
- ./var/postgres_data:/var/lib/postgresql/data
|
- ./var/postgres_data:/var/lib/postgresql/data
|
||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.postgres.entrypoints=postgres"
|
||||||
|
- "traefik.http.routers.postgres.rule=Host(`calendi-postgres.test`)"
|
||||||
|
- "traefik.http.services.postgres.loadbalancer.server.port=5432"
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
proxy:
|
proxy:
|
||||||
|
|||||||
@ -10,7 +10,13 @@ RUN apt-get update && apt-get install -y \
|
|||||||
apt-transport-https \
|
apt-transport-https \
|
||||||
software-properties-common \
|
software-properties-common \
|
||||||
nginx \
|
nginx \
|
||||||
unzip
|
unzip \
|
||||||
|
tzdata
|
||||||
|
|
||||||
|
# Set timezone to Germany
|
||||||
|
RUN ln -fs /usr/share/zoneinfo/Europe/Berlin /etc/localtime && \
|
||||||
|
dpkg-reconfigure -f noninteractive tzdata && \
|
||||||
|
echo "Europe/Berlin" > /etc/timezone
|
||||||
|
|
||||||
# Add PHP repository
|
# Add PHP repository
|
||||||
RUN wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg \
|
RUN wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg \
|
||||||
|
|||||||
@ -9,6 +9,11 @@ server {
|
|||||||
try_files $uri $uri/ /index.php$is_args$args;
|
try_files $uri $uri/ /index.php$is_args$args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# PHP Backend API
|
||||||
|
location /_profiler {
|
||||||
|
try_files $uri $uri/ /index.php$is_args$args;
|
||||||
|
}
|
||||||
|
|
||||||
location ~ \.php$ {
|
location ~ \.php$ {
|
||||||
fastcgi_pass 127.0.0.1:9000;
|
fastcgi_pass 127.0.0.1:9000;
|
||||||
fastcgi_index index.php;
|
fastcgi_index index.php;
|
||||||
|
|||||||
@ -26,19 +26,16 @@ export type Event = {
|
|||||||
allDay: boolean;
|
allDay: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CreateEventRequest = {
|
export type PersistEventRequest = {
|
||||||
title: string;
|
draft: EventDraft;
|
||||||
description?: string;
|
id?: string;
|
||||||
start: string;
|
|
||||||
end: string;
|
|
||||||
allDay?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Event endpoints
|
// Event endpoints
|
||||||
export const getEvents = () => get<Event[]>('/api/events');
|
export const getEvents = () => get<Event[]>('/api/events');
|
||||||
export const getEvent = (id: string) => get<Event>(`/api/events/${id}`);
|
export const getEvent = (id: string) => get<Event>(`/api/events/${id}`);
|
||||||
export const createEvent = (data: CreateEventRequest) => post<Event>('/api/events', data);
|
export const persistEvent = (data: PersistEventRequest) => post<Event>('/api/events', data);
|
||||||
export const updateEvent = (id: string, data: Partial<CreateEventRequest>) => put<Event>(`/api/events/${id}`, data);
|
export const updateEvent = (id: string, data: Partial<PersistEventRequest>) => put<Event>(`/api/events/${id}`, data);
|
||||||
export const deleteEvent = (id: string) => del<void>(`/api/events/${id}`);
|
export const deleteEvent = (id: string) => del<void>(`/api/events/${id}`);
|
||||||
|
|
||||||
// Event draft types
|
// Event draft types
|
||||||
|
|||||||
@ -43,25 +43,4 @@ export function useApi<T, P extends unknown[]>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
return [execute, state];
|
return [execute, state];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Example usage:
|
|
||||||
//
|
|
||||||
// function UserList() {
|
|
||||||
// const [fetchUsers, { data: users, loading, error }] = useApi(getUsers, []);
|
|
||||||
//
|
|
||||||
// useEffect(() => {
|
|
||||||
// fetchUsers();
|
|
||||||
// }, [fetchUsers]);
|
|
||||||
//
|
|
||||||
// if (loading) return <div>Loading...</div>;
|
|
||||||
// if (error) return <div>Error: {error}</div>;
|
|
||||||
//
|
|
||||||
// return (
|
|
||||||
// <ul>
|
|
||||||
// {users?.map(user => (
|
|
||||||
// <li key={user.id}>{user.name}</li>
|
|
||||||
// ))}
|
|
||||||
// </ul>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { EventDraft, createEvent } from '../../lib/api/endpoints';
|
import { EventDraft, persistEvent } from '../../lib/api/endpoints';
|
||||||
import LoadingSpinner from '../../components/ui/LoadingSpinner';
|
import LoadingSpinner from '../../components/ui/LoadingSpinner';
|
||||||
import './EditEvent.css';
|
import './EditEvent.css';
|
||||||
|
|
||||||
@ -85,14 +85,17 @@ const EditEvent: React.FC = () => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const eventData = {
|
const eventData = {
|
||||||
title,
|
draft: {
|
||||||
description,
|
id: id || '',
|
||||||
start: startDate ? formatForServer(startDate, startTime || '00:00') : new Date().toISOString(),
|
title,
|
||||||
end: endDate ? formatForServer(endDate, endTime || '00:00') : new Date().toISOString(),
|
description,
|
||||||
allDay
|
start: startDate ? formatForServer(startDate, startTime || '00:00') : new Date().toISOString(),
|
||||||
|
end: endDate ? formatForServer(endDate, endTime || '00:00') : new Date().toISOString(),
|
||||||
|
allDay
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const savedEvent = await createEvent(eventData);
|
const savedEvent = await persistEvent(eventData);
|
||||||
console.log('Event created:', savedEvent);
|
console.log('Event created:', savedEvent);
|
||||||
|
|
||||||
// Clear the draft from sessionStorage
|
// Clear the draft from sessionStorage
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user