diff --git a/src/Bundle/ChillMainBundle/Controller/UserGroupApiController.php b/src/Bundle/ChillMainBundle/Controller/UserGroupApiController.php new file mode 100644 index 000000000..602b84ec5 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/UserGroupApiController.php @@ -0,0 +1,16 @@ +getRepository(User::class)->findOneBy(['username' => 'center a_social']); + $centerBSocial = $manager->getRepository(User::class)->findOneBy(['username' => 'center b_social']); + $multiCenter = $manager->getRepository(User::class)->findOneBy(['username' => 'multi_center']); + $administrativeA = $manager->getRepository(User::class)->findOneBy(['username' => 'center a_administrative']); + $administrativeB = $manager->getRepository(User::class)->findOneBy(['username' => 'center b_administrative']); + + $level1 = $this->generateLevelGroup('Niveau 1', '#eec84aff', '#000000ff', 'level'); + $level1->addUser($centerASocial)->addUser($centerBSocial); + $manager->persist($level1); + + $level2 = $this->generateLevelGroup('Niveau 2', ' #e2793dff', '#000000ff', 'level'); + $level2->addUser($multiCenter); + $manager->persist($level2); + + $level3 = $this->generateLevelGroup('Niveau 3', ' #df4949ff', '#000000ff', 'level'); + $level3->addUser($multiCenter); + $manager->persist($level3); + + $tss = $this->generateLevelGroup('Travailleur sociaux', '#43b29dff', '#000000ff', ''); + $tss->addUser($multiCenter)->addUser($centerASocial)->addUser($centerBSocial); + $manager->persist($tss); + $admins = $this->generateLevelGroup('Administratif', '#334d5cff', '#000000ff', ''); + $admins->addUser($administrativeA)->addUser($administrativeB); + $manager->persist($admins); + + $manager->flush(); + } + + private function generateLevelGroup(string $title, string $backgroundColor, string $foregroundColor, string $excludeKey): UserGroup + { + $userGroup = new UserGroup(); + + return $userGroup + ->setLabel(['fr' => $title]) + ->setBackgroundColor($backgroundColor) + ->setForegroundColor($foregroundColor) + ->setExcludeKey($excludeKey) + ; + } +} diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 9d788c64e..97df404af 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -24,6 +24,7 @@ use Chill\MainBundle\Controller\LocationTypeController; use Chill\MainBundle\Controller\NewsItemController; use Chill\MainBundle\Controller\RegroupmentController; use Chill\MainBundle\Controller\UserController; +use Chill\MainBundle\Controller\UserGroupApiController; use Chill\MainBundle\Controller\UserJobApiController; use Chill\MainBundle\Controller\UserJobController; use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface; @@ -59,6 +60,7 @@ use Chill\MainBundle\Entity\LocationType; use Chill\MainBundle\Entity\NewsItem; use Chill\MainBundle\Entity\Regroupment; use Chill\MainBundle\Entity\User; +use Chill\MainBundle\Entity\UserGroup; use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Form\CenterType; use Chill\MainBundle\Form\CivilityType; @@ -803,6 +805,21 @@ class ChillMainExtension extends Extension implements ], ], ], + [ + 'class' => UserGroup::class, + 'controller' => UserGroupApiController::class, + 'name' => 'user-group', + 'base_path' => '/api/1.0/main/user-group', + 'base_role' => 'ROLE_USER', + 'actions' => [ + '_index' => [ + 'methods' => [ + Request::METHOD_GET => true, + Request::METHOD_HEAD => true, + ], + ], + ], + ], ], ]); } diff --git a/src/Bundle/ChillMainBundle/Entity/UserGroup.php b/src/Bundle/ChillMainBundle/Entity/UserGroup.php new file mode 100644 index 000000000..03da74326 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Entity/UserGroup.php @@ -0,0 +1,151 @@ + UserGroup::class])] +class UserGroup +{ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)] + #[Serializer\Groups(['read'])] + private ?int $id = null; + + #[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]'])] + #[Serializer\Groups(['read'])] + private array $label = []; + + /** + * @var \Doctrine\Common\Collections\Collection + */ + #[ORM\ManyToMany(targetEntity: User::class)] + #[ORM\JoinTable(name: 'chill_main_user_group_user')] + private Collection $users; + + #[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => '#ffffffff'])] + #[Serializer\Groups(['read'])] + private string $backgroundColor = '#ffffffff'; + + #[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => '#000000ff'])] + #[Serializer\Groups(['read'])] + private string $foregroundColor = '#000000ff'; + + /** + * Groups with same exclude key are mutually exclusive: adding one in a many-to-one relationship + * will exclude others. + * + * An empty string means "no exclusion" + */ + #[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])] + #[Serializer\Groups(['read'])] + private string $excludeKey = ''; + + public function __construct() + { + $this->users = new ArrayCollection(); + } + + public function addUser(User $user): self + { + if (!$this->users->contains($user)) { + $this->users[] = $user; + } + + return $this; + } + + public function removeUser(User $user): self + { + if ($this->users->contains($user)) { + $this->users->removeElement($user); + } + + return $this; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getLabel(): array + { + return $this->label; + } + + public function getUsers(): Collection + { + return $this->users; + } + + public function getForegroundColor(): string + { + return $this->foregroundColor; + } + + public function getExcludeKey(): string + { + return $this->excludeKey; + } + + public function getBackgroundColor(): string + { + return $this->backgroundColor; + } + + public function setForegroundColor(string $foregroundColor): self + { + $this->foregroundColor = $foregroundColor; + + return $this; + } + + public function setBackgroundColor(string $backgroundColor): self + { + $this->backgroundColor = $backgroundColor; + + return $this; + } + + public function setExcludeKey(string $excludeKey): self + { + $this->excludeKey = $excludeKey; + + return $this; + } + + public function setLabel(array $label): self + { + $this->label = $label; + + return $this; + } + + /** + * Checks if the current object is an instance of the UserGroup class. + * + * In use in twig template, to discriminate when there an object can be polymorphic. + * + * @return bool returns true if the current object is an instance of UserGroup, false otherwise + */ + public function isUserGroup(): bool + { + return true; + } +} diff --git a/src/Bundle/ChillMainBundle/Resources/public/types.ts b/src/Bundle/ChillMainBundle/Resources/public/types.ts index 304ebd8e4..8d2ea2ded 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/types.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/types.ts @@ -1,164 +1,175 @@ export interface DateTime { - datetime: string; - datetime8601: string + datetime: string; + datetime8601: string; } export interface Civility { - id: number; - // TODO + id: number; + // TODO } export interface Job { - id: number; - type: "user_job"; - label: { - "fr": string; // could have other key. How to do that in ts ? - } + id: number; + type: "user_job"; + label: { + fr: string; // could have other key. How to do that in ts ? + }; } export interface Center { - id: number; - type: "center"; - name: string; + id: number; + type: "center"; + name: string; } export interface Scope { - id: number; - type: "scope"; - name: { - "fr": string - } + id: number; + type: "scope"; + name: { + fr: string; + }; } export interface User { - type: "user"; - id: number; - username: string; - text: string; - text_without_absence: string; - email: string; - user_job: Job; - label: string; - // todo: mainCenter; mainJob; etc.. + type: "user"; + id: number; + username: string; + text: string; + text_without_absence: string; + email: string; + user_job: Job; + label: string; + // todo: mainCenter; mainJob; etc.. } +export interface UserGroup { + type: "chill_main_user_group" | "user_group"; + id: number; + label: TranslatableString; + backgroundColor: string; + foregroundColor: string; + excludeKey: string; +} + +export type UserGroupOrUser = User | UserGroup; + export interface UserAssociatedInterface { - type: "user"; - id: number; -}; - -export type TranslatableString = { - fr?: string; - nl?: string; + type: "user"; + id: number; } +export type TranslatableString = { + fr?: string; + nl?: string; +}; + export interface Postcode { - id: number; - name: string; - code: string; - center: Point; + id: number; + name: string; + code: string; + center: Point; } export type Point = { - type: "Point"; - coordinates: [lat: number, lon: number]; -} + type: "Point"; + coordinates: [lat: number, lon: number]; +}; export interface Country { - id: number; - name: TranslatableString; - code: string; + id: number; + name: TranslatableString; + code: string; } -export type AddressRefStatus = 'match'|'to_review'|'reviewed'; +export type AddressRefStatus = "match" | "to_review" | "reviewed"; export interface Address { - type: "address"; - address_id: number; - text: string; - street: string; - streetNumber: string; - postcode: Postcode; - country: Country; - floor: string | null; - corridor: string | null; - steps: string | null; - flat: string | null; - buildingName: string | null; - distribution: string | null; - extra: string | null; - confidential: boolean; - lines: string[]; - addressReference: AddressReference | null; - validFrom: DateTime; - validTo: DateTime | null; - point: Point | null; - refStatus: AddressRefStatus; - isNoAddress: boolean; + type: "address"; + address_id: number; + text: string; + street: string; + streetNumber: string; + postcode: Postcode; + country: Country; + floor: string | null; + corridor: string | null; + steps: string | null; + flat: string | null; + buildingName: string | null; + distribution: string | null; + extra: string | null; + confidential: boolean; + lines: string[]; + addressReference: AddressReference | null; + validFrom: DateTime; + validTo: DateTime | null; + point: Point | null; + refStatus: AddressRefStatus; + isNoAddress: boolean; } export interface AddressWithPoint extends Address { - point: Point + point: Point; } export interface AddressReference { - id: number; - createdAt: DateTime | null; - deletedAt: DateTime | null; - municipalityCode: string; - point: Point; - postcode: Postcode; - refId: string; - source: string; - street: string; - streetNumber: string; - updatedAt: DateTime | null; + id: number; + createdAt: DateTime | null; + deletedAt: DateTime | null; + municipalityCode: string; + point: Point; + postcode: Postcode; + refId: string; + source: string; + street: string; + streetNumber: string; + updatedAt: DateTime | null; } export interface SimpleGeographicalUnit { - id: number; - layerId: number; - unitName: string; - unitRefId: string; + id: number; + layerId: number; + unitName: string; + unitRefId: string; } export interface GeographicalUnitLayer { - id: number; - name: TranslatableString; - refId: string; + id: number; + name: TranslatableString; + refId: string; } export interface Location { - type: "location"; - id: number; - active: boolean; - address: Address | null; - availableForUsers: boolean; - createdAt: DateTime | null; - createdBy: User | null; - updatedAt: DateTime | null; - updatedBy: User | null; - email: string | null - name: string; - phonenumber1: string | null; - phonenumber2: string | null; - locationType: LocationType; + type: "location"; + id: number; + active: boolean; + address: Address | null; + availableForUsers: boolean; + createdAt: DateTime | null; + createdBy: User | null; + updatedAt: DateTime | null; + updatedBy: User | null; + email: string | null; + name: string; + phonenumber1: string | null; + phonenumber2: string | null; + locationType: LocationType; } export interface LocationAssociated { - type: "location"; - id: number; + type: "location"; + id: number; } export interface LocationType { - type: "location-type"; - id: number; - active: boolean; - addressRequired: "optional" | "required"; - availableForUsers: boolean; - editableByUsers: boolean; - contactData: "optional" | "required"; - title: TranslatableString; + type: "location-type"; + id: number; + active: boolean; + addressRequired: "optional" | "required"; + availableForUsers: boolean; + editableByUsers: boolean; + contactData: "optional" | "required"; + title: TranslatableString; } export interface NewsItemType { diff --git a/src/Bundle/ChillMainBundle/Resources/views/Entity/user_group.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Entity/user_group.html.twig new file mode 100644 index 000000000..cb6c3be40 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/Entity/user_group.html.twig @@ -0,0 +1 @@ +{{ user_group.label|localize_translatable_string }} diff --git a/src/Bundle/ChillMainBundle/Templating/Entity/UserGroupRender.php b/src/Bundle/ChillMainBundle/Templating/Entity/UserGroupRender.php new file mode 100644 index 000000000..b63fca8cf --- /dev/null +++ b/src/Bundle/ChillMainBundle/Templating/Entity/UserGroupRender.php @@ -0,0 +1,38 @@ +environment->render('@ChillMain/Entity/user_group.html.twig', ['user_group' => $entity]); + } + + public function renderString($entity, array $options): string + { + /* @var $entity UserGroup */ + return $this->translatableStringHelper->localize($entity->getLabel()); + } + + public function supports(object $entity, array $options): bool + { + return $entity instanceof UserGroup; + } +} diff --git a/src/Bundle/ChillMainBundle/Templating/Entity/UserGroupRenderInterface.php b/src/Bundle/ChillMainBundle/Templating/Entity/UserGroupRenderInterface.php new file mode 100644 index 000000000..7ebf70028 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Templating/Entity/UserGroupRenderInterface.php @@ -0,0 +1,14 @@ +getExcludeKey()][] = $gr; + } + } + + foreach ($groups as $excludeKey => $groupByKey) { + if ('' === $excludeKey) { + continue; + } + + if (1 < count($groupByKey)) { + $excludedGroups = implode( + ', ', + array_map( + fn (UserGroup $group) => $this->translatableStringHelper->localize($group->getLabel()), + $groupByKey + ) + ); + + $this->context + ->buildViolation($constraint->message) + ->setCode($constraint->code) + ->setParameters(['excluded_groups' => $excludedGroups]) + ->addViolation(); + } + } + } +} diff --git a/src/Bundle/ChillMainBundle/chill.api.specs.yaml b/src/Bundle/ChillMainBundle/chill.api.specs.yaml index 1abdfc72e..b31669043 100644 --- a/src/Bundle/ChillMainBundle/chill.api.specs.yaml +++ b/src/Bundle/ChillMainBundle/chill.api.specs.yaml @@ -5,8 +5,8 @@ info: title: "Chill api" description: "Api documentation for chill. Currently, work in progress" servers: - - url: "/api" - description: "Your current dev server" + - url: "/api" + description: "Your current dev server" components: schemas: @@ -29,6 +29,42 @@ components: type: string text: type: string + UserById: + type: object + properties: + id: + type: integer + type: + type: string + enum: + - user + UserGroup: + type: object + properties: + id: + type: integer + type: + type: string + enum: + - user_group + label: + type: object + additionalProperties: true + backgroundColor: + type: string + foregroundColor: + type: string + exclusionKey: + type: string + UserGroupById: + type: object + properties: + id: + type: integer + type: + type: string + enum: + - user_group Center: type: object properties: @@ -181,25 +217,25 @@ paths: The results are ordered by relevance, from the most to the lowest relevant. parameters: - - name: q - in: query - required: true - description: the pattern to search - schema: - type: string - - name: type[] - in: query - required: true - description: the type entities amongst the search is performed - schema: - type: array - items: - type: string - enum: - - person - - thirdparty - - user - - household + - name: q + in: query + required: true + description: the pattern to search + schema: + type: string + - name: type[] + in: query + required: true + description: the type entities amongst the search is performed + schema: + type: array + items: + type: string + enum: + - person + - thirdparty + - user + - household responses: 200: description: "OK" @@ -236,7 +272,7 @@ paths: minItems: 2 maxItems: 2 postcode: - $ref: "#/components/schemas/PostalCode" + $ref: '#/components/schemas/PostalCode' steps: type: string street: @@ -260,14 +296,14 @@ paths: - address summary: Return an address by id parameters: - - name: id - in: path - required: true - description: The address id - schema: - type: integer - format: integer - minimum: 1 + - name: id + in: path + required: true + description: The address id + schema: + type: integer + format: integer + minimum: 1 responses: 200: description: "ok" @@ -284,14 +320,14 @@ paths: - address summary: patch an address parameters: - - name: id - in: path - required: true - description: The address id - schema: - type: integer - format: integer - minimum: 1 + - name: id + in: path + required: true + description: The address id + schema: + type: integer + format: integer + minimum: 1 requestBody: required: true content: @@ -349,14 +385,14 @@ paths: - address summary: Duplicate an existing address parameters: - - name: id - in: path - required: true - description: The address id that will be duplicated - schema: - type: integer - format: integer - minimum: 1 + - name: id + in: path + required: true + description: The address id that will be duplicated + schema: + type: integer + format: integer + minimum: 1 responses: 200: description: "ok" @@ -375,12 +411,12 @@ paths: - address summary: Return a list of all reference addresses parameters: - - in: query - name: postal_code - required: false - schema: - type: integer - description: The id of a postal code to filter the reference addresses + - in: query + name: postal_code + required: false + schema: + type: integer + description: The id of a postal code to filter the reference addresses responses: 200: description: "ok" @@ -390,14 +426,14 @@ paths: - address summary: Return a reference address by id parameters: - - name: id - in: path - required: true - description: The reference address id - schema: - type: integer - format: integer - minimum: 1 + - name: id + in: path + required: true + description: The reference address id + schema: + type: integer + format: integer + minimum: 1 responses: 200: description: "ok" @@ -417,20 +453,20 @@ paths: - search summary: Return a reference address by id parameters: - - name: id - in: path - required: true - description: The reference address id - schema: - type: integer - format: integer - minimum: 1 - - name: q - in: query - required: true - description: The search pattern - schema: - type: string + - name: id + in: path + required: true + description: The reference address id + schema: + type: integer + format: integer + minimum: 1 + - name: q + in: query + required: true + description: The search pattern + schema: + type: string responses: 200: description: "ok" @@ -450,12 +486,12 @@ paths: - address summary: Return a list of all postal-code parameters: - - in: query - name: country - required: false - schema: - type: integer - description: The id of a country to filter the postal code + - in: query + name: country + required: false + schema: + type: integer + description: The id of a country to filter the postal code responses: 200: description: "ok" @@ -494,14 +530,14 @@ paths: - address summary: Return a postal code by id parameters: - - name: id - in: path - required: true - description: The postal code id - schema: - type: integer - format: integer - minimum: 1 + - name: id + in: path + required: true + description: The postal code id + schema: + type: integer + format: integer + minimum: 1 responses: 200: description: "ok" @@ -521,18 +557,18 @@ paths: - search summary: Search a postal code parameters: - - name: q - in: query - required: true - description: The search pattern - schema: - type: string - - name: country - in: query - required: false - description: The country id - schema: - type: integer + - name: q + in: query + required: true + description: The search pattern + schema: + type: string + - name: country + in: query + required: false + description: The country id + schema: + type: integer responses: 200: description: "ok" @@ -559,14 +595,14 @@ paths: - address summary: Return a country by id parameters: - - name: id - in: path - required: true - description: The country id - schema: - type: integer - format: integer - minimum: 1 + - name: id + in: path + required: true + description: The country id + schema: + type: integer + format: integer + minimum: 1 responses: 200: description: "ok" @@ -609,14 +645,14 @@ paths: - user summary: Return a user by id parameters: - - name: id - in: path - required: true - description: The user id - schema: - type: integer - format: integer - minimum: 1 + - name: id + in: path + required: true + description: The user id + schema: + type: integer + format: integer + minimum: 1 responses: 200: description: "ok" @@ -646,14 +682,14 @@ paths: - scope summary: return a list of scopes parameters: - - name: id - in: path - required: true - description: The scope id - schema: - type: integer - format: integer - minimum: 1 + - name: id + in: path + required: true + description: The scope id + schema: + type: integer + format: integer + minimum: 1 responses: 200: description: "ok" @@ -721,14 +757,14 @@ paths: - location summary: Return the given location parameters: - - name: id - in: path - required: true - description: The location id - schema: - type: integer - format: integer - minimum: 1 + - name: id + in: path + required: true + description: The location id + schema: + type: integer + format: integer + minimum: 1 responses: 200: description: "ok" @@ -788,14 +824,14 @@ paths: - notification summary: mark a notification as read parameters: - - name: id - in: path - required: true - description: The notification id - schema: - type: integer - format: integer - minimum: 1 + - name: id + in: path + required: true + description: The notification id + schema: + type: integer + format: integer + minimum: 1 responses: 202: description: "accepted" @@ -807,14 +843,14 @@ paths: - notification summary: mark a notification as unread parameters: - - name: id - in: path - required: true - description: The notification id - schema: - type: integer - format: integer - minimum: 1 + - name: id + in: path + required: true + description: The notification id + schema: + type: integer + format: integer + minimum: 1 responses: 202: description: "accepted" @@ -846,7 +882,7 @@ paths: type: array items: type: integer - example: [1, 2, 3] # Example array of IDs + example: [ 1, 2, 3 ] # Example array of IDs responses: "202": description: Notifications marked as unread successfully @@ -934,6 +970,22 @@ paths: schema: type: array items: - $ref: "#/components/schemas/NewsItem" + $ref: '#/components/schemas/NewsItem' + 403: + description: "Unauthorized" + /1.0/main/user-group.json: + get: + tags: + - user-group + summary: Return a list of users-groups + responses: + 200: + description: "ok" + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/UserGroup' 403: description: "Unauthorized" diff --git a/src/Bundle/ChillMainBundle/config/services/templating.yaml b/src/Bundle/ChillMainBundle/config/services/templating.yaml index 281d4ad23..04117bf16 100644 --- a/src/Bundle/ChillMainBundle/config/services/templating.yaml +++ b/src/Bundle/ChillMainBundle/config/services/templating.yaml @@ -56,6 +56,10 @@ services: Chill\MainBundle\Templating\Entity\UserRender: ~ + Chill\MainBundle\Templating\Entity\UserGroupRender: ~ + Chill\MainBundle\Templating\Entity\UserGroupRenderInterface: + alias: Chill\MainBundle\Templating\Entity\UserGroupRender + Chill\MainBundle\Templating\Listing\: resource: './../../Templating/Listing' diff --git a/src/Bundle/ChillMainBundle/config/services/validator.yaml b/src/Bundle/ChillMainBundle/config/services/validator.yaml index b3b60b9d6..32b8903cc 100644 --- a/src/Bundle/ChillMainBundle/config/services/validator.yaml +++ b/src/Bundle/ChillMainBundle/config/services/validator.yaml @@ -3,6 +3,9 @@ services: autowire: true autoconfigure: true + Chill\MainBundle\Validation\: + resource: '../../Validation' + chill_main.validator_user_circle_consistency: class: Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistencyValidator arguments: diff --git a/src/Bundle/ChillMainBundle/migrations/Version20240416145021.php b/src/Bundle/ChillMainBundle/migrations/Version20240416145021.php new file mode 100644 index 000000000..f2f8d37dd --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20240416145021.php @@ -0,0 +1,41 @@ +addSql('CREATE SEQUENCE chill_main_user_group_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE chill_main_user_group (id INT NOT NULL, label JSON DEFAULT \'[]\' NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE TABLE chill_main_user_group_user (usergroup_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY(usergroup_id, user_id))'); + $this->addSql('CREATE INDEX IDX_1E07F044D2112630 ON chill_main_user_group_user (usergroup_id)'); + $this->addSql('CREATE INDEX IDX_1E07F044A76ED395 ON chill_main_user_group_user (user_id)'); + $this->addSql('ALTER TABLE chill_main_user_group_user ADD CONSTRAINT FK_1E07F044D2112630 FOREIGN KEY (usergroup_id) REFERENCES chill_main_user_group (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_main_user_group_user ADD CONSTRAINT FK_1E07F044A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP SEQUENCE chill_main_user_group_id_seq'); + $this->addSql('DROP TABLE chill_main_user_group_user'); + $this->addSql('DROP TABLE chill_main_user_group'); + } +} diff --git a/src/Bundle/ChillMainBundle/migrations/Version20240422091752.php b/src/Bundle/ChillMainBundle/migrations/Version20240422091752.php new file mode 100644 index 000000000..960b3cc79 --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20240422091752.php @@ -0,0 +1,41 @@ +addSql('ALTER TABLE chill_main_user_group ADD backgroundColor TEXT DEFAULT \'#ffffffff\' NOT NULL'); + $this->addSql('ALTER TABLE chill_main_user_group ADD foregroundColor TEXT DEFAULT \'#000000ff\' NOT NULL'); + $this->addSql('ALTER TABLE chill_main_user_group ADD excludeKey TEXT DEFAULT \'\' NOT NULL'); + $this->addSql('ALTER INDEX idx_1e07f044d2112630 RENAME TO IDX_738BC82BD2112630'); + $this->addSql('ALTER INDEX idx_1e07f044a76ed395 RENAME TO IDX_738BC82BA76ED395'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_main_user_group DROP backgroundColor'); + $this->addSql('ALTER TABLE chill_main_user_group DROP foregroundColor'); + $this->addSql('ALTER TABLE chill_main_user_group DROP excludeKey'); + $this->addSql('ALTER INDEX idx_738bc82bd2112630 RENAME TO idx_1e07f044d2112630'); + $this->addSql('ALTER INDEX idx_738bc82ba76ed395 RENAME TO idx_1e07f044a76ed395'); + } +} diff --git a/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/badge.scss b/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/badge.scss index 072f13949..d1faf5614 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/badge.scss +++ b/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/badge.scss @@ -3,8 +3,10 @@ */ span.badge-user, +span.badge-user-group, span.badge-person, span.badge-thirdparty { + margin: 0.2rem 0.1rem; display: inline-block; padding: 0 0.5em !important; background-color: $white; @@ -18,6 +20,11 @@ span.badge-thirdparty { } } +span.badge-user-group { + font-weight: 600; + border-width: 0px; +} + span.badge-user { border-bottom-width: 1px; &.system {