Implement project + remove useless config

This commit is contained in:
Nicolò P 2025-04-02 17:16:54 +02:00
parent 3422412bbf
commit 34d1506483
18 changed files with 502 additions and 70 deletions

View File

@ -6,6 +6,3 @@ import './bootstrap.js';
* which should already be in your base.html.twig.
*/
import './styles/app.css';
import API_CONFIG from "./config.js";
window.API_CONFIG = API_CONFIG;

View File

@ -77,8 +77,8 @@ BIM.loadIfc = async function (buffer, name) {
}
const fragments = this.components.get(OBC.FragmentsManager);
const fragmentIfcLoader = this.components.get(OBC.IfcLoader);
const classifier = this.components.get(OBC.Classifier);
// NOTE: loads web-ifc WASM from https://unpkg.com/web-ifc@0.0.53/
await fragmentIfcLoader.setup();
@ -107,14 +107,20 @@ BIM.loadIfc = async function (buffer, name) {
model.name = name;
this.world.scene.three.add(model);
// To actually add the model to the scene...
for (const fragment of model.items) {
this.world.meshes.add(fragment.mesh);
}
classifier.byEntity(model);
const entities = classifier.list.entities;
// Useful?
this.fragments = fragments;
this.model = model;
console.log(entities);
return model;
}
@ -147,6 +153,7 @@ BIM.setupHighligther = async function (model) {
li.innerHTML = `
<ul>
<li><strong>Name</strong>: ${testProp['Name'].value}</span>
<li><strong>Type</strong>: ${testProp['ObjectType']?.value}</span>
<li><strong>Tag</strong>: ${testProp['Tag'].value}</li>
</ul>
`;

View File

@ -1,4 +1,15 @@
{
"controllers": [],
"controllers": {
"@symfony/ux-turbo": {
"turbo-core": {
"enabled": true,
"fetch": "eager"
},
"mercure-turbo-stream": {
"enabled": false,
"fetch": "eager"
}
}
},
"entrypoints": []
}

View File

@ -11,8 +11,6 @@ export default class extends Controller {
'buildingForm'
];
API_BASE = window.API_CONFIG.dev;
async submit(event) {
event.preventDefault();
@ -22,10 +20,7 @@ export default class extends Controller {
'POST'
);
const res = await this.send(
`${this.API_BASE}/api/buildings`,
options
);
const res = await this.send('/project/create', options);
if (res.id) {
this.buildingTarget.setAttribute('data-id', res.id);

View File

@ -30,6 +30,7 @@
"symfony/serializer": "7.1.*",
"symfony/stimulus-bundle": "^2.23",
"symfony/twig-bundle": "7.1.*",
"symfony/ux-turbo": "^2.23",
"symfony/validator": "7.1.*",
"symfony/yaml": "7.1.*"
},

104
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "4e98bff9162121f1c4fb1d257c7b7411",
"content-hash": "87b4a80041b3e1a8c74c5e68788219c9",
"packages": [
{
"name": "api-platform/doctrine-common",
@ -7024,6 +7024,104 @@
],
"time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/ux-turbo",
"version": "v2.23.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/ux-turbo.git",
"reference": "db96cf04d70a8c820671ce55530e8bf641ada33f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/ux-turbo/zipball/db96cf04d70a8c820671ce55530e8bf641ada33f",
"reference": "db96cf04d70a8c820671ce55530e8bf641ada33f",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/stimulus-bundle": "^2.9.1"
},
"conflict": {
"symfony/flex": "<1.13"
},
"require-dev": {
"dbrekelmans/bdi": "dev-main",
"doctrine/doctrine-bundle": "^2.4.3",
"doctrine/orm": "^2.8 | 3.0",
"phpstan/phpstan": "^1.10",
"symfony/asset-mapper": "^6.4|^7.0",
"symfony/debug-bundle": "^5.4|^6.0|^7.0",
"symfony/expression-language": "^5.4|^6.0|^7.0",
"symfony/form": "^5.4|^6.0|^7.0",
"symfony/framework-bundle": "^6.4|^7.0",
"symfony/mercure-bundle": "^0.3.7",
"symfony/messenger": "^5.4|^6.0|^7.0",
"symfony/panther": "^2.1",
"symfony/phpunit-bridge": "^5.4|^6.0|^7.0",
"symfony/process": "^5.4|6.3.*|^7.0",
"symfony/property-access": "^5.4|^6.0|^7.0",
"symfony/security-core": "^5.4|^6.0|^7.0",
"symfony/stopwatch": "^5.4|^6.0|^7.0",
"symfony/twig-bundle": "^6.4|^7.0",
"symfony/ux-twig-component": "^2.21",
"symfony/web-profiler-bundle": "^5.4|^6.0|^7.0"
},
"type": "symfony-bundle",
"extra": {
"thanks": {
"url": "https://github.com/symfony/ux",
"name": "symfony/ux"
}
},
"autoload": {
"psr-4": {
"Symfony\\UX\\Turbo\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kévin Dunglas",
"email": "kevin@dunglas.fr"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Hotwire Turbo integration for Symfony",
"homepage": "https://symfony.com",
"keywords": [
"hotwire",
"javascript",
"mercure",
"symfony-ux",
"turbo",
"turbo-stream"
],
"support": {
"source": "https://github.com/symfony/ux-turbo/tree/v2.23.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": "2025-02-06T08:47:30+00:00"
},
{
"name": "symfony/validator",
"version": "v7.1.11",
@ -9886,7 +9984,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"stability-flags": [],
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
@ -9894,6 +9992,6 @@
"ext-ctype": "*",
"ext-iconv": "*"
},
"platform-dev": {},
"platform-dev": [],
"plugin-api-version": "2.6.0"
}

View File

@ -11,4 +11,5 @@ return [
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
Symfony\UX\Turbo\TurboBundle::class => ['all' => true],
];

View File

@ -332,14 +332,17 @@ return [
'@thatopen/components' => [
'version' => '2.4.5',
],
'three' => [
'version' => '0.160.1',
],
'@thatopen/components-front' => [
'version' => '2.4.4',
'version' => '2.4.5',
],
'bootstrap-icons/font/bootstrap-icons.min.css' => [
'version' => '1.11.3',
'type' => 'css',
],
'three' => [
'version' => '0.160.1',
],
'@hotwired/turbo' => [
'version' => '7.3.0',
],
];

View File

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250402061353 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
ALTER TABLE project DROP FOREIGN KEY FK_2FB3D0EEA76ED395
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE project DROP FOREIGN KEY FK_2FB3D0EE4D2A7E12
SQL);
$this->addSql(<<<'SQL'
DROP TABLE project
SQL);
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
CREATE TABLE project (id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, building_id INT NOT NULL, ifc VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, INDEX IDX_2FB3D0EEA76ED395 (user_id), INDEX IDX_2FB3D0EE4D2A7E12 (building_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB COMMENT = ''
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE project ADD CONSTRAINT FK_2FB3D0EEA76ED395 FOREIGN KEY (user_id) REFERENCES user (id)
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE project ADD CONSTRAINT FK_2FB3D0EE4D2A7E12 FOREIGN KEY (building_id) REFERENCES architettura (id)
SQL);
}
}

View File

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250402061726 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
CREATE TABLE project (id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, building_id INT NOT NULL, ifc VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', last_modified DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)', INDEX IDX_2FB3D0EEA76ED395 (user_id), INDEX IDX_2FB3D0EE4D2A7E12 (building_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE project ADD CONSTRAINT FK_2FB3D0EEA76ED395 FOREIGN KEY (user_id) REFERENCES user (id)
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE project ADD CONSTRAINT FK_2FB3D0EE4D2A7E12 FOREIGN KEY (building_id) REFERENCES architettura (id)
SQL);
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
ALTER TABLE project DROP FOREIGN KEY FK_2FB3D0EEA76ED395
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE project DROP FOREIGN KEY FK_2FB3D0EE4D2A7E12
SQL);
$this->addSql(<<<'SQL'
DROP TABLE project
SQL);
}
}

View File

@ -0,0 +1,70 @@
<?php
namespace App\Controller;
use App\Entity\Building;
use App\Entity\Project;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
final class ProjectController extends AbstractController
{
#[Route('/project', name: 'app_project')]
public function index(): Response
{
return $this->render('project/index.html.twig', [
'controller_name' => 'ProjectController',
]);
}
#[Route('/projects', name: 'app_projects')]
public function projects(#[CurrentUser] User $user, EntityManagerInterface $em): Response
{
$repo = $em->getRepository(Project::class);
$projects = $repo->findBy(['user' => $user]);
return $this->render('project/index.html.twig', [
'controller_name' => 'ProjectController',
'projects' => $projects,
]);
}
#[Route('/project/create', name: 'app_project_create', methods: ['POST'])]
public function create(
EntityManagerInterface $em,
Request $request,
#[CurrentUser()]
User $user
): JsonResponse
{
$data = $request->getPayload();
$datetime = new \DateTimeImmutable();
$building = new Building();
$building->setName($data->get('name'));
$em->persist($building);
$em->flush();
$project = new Project();
$project->setUser($user);
$project->setBuilding($building);
$project->setCreatedAt($datetime);
$project->setLastModified($datetime);
$em->persist($project);
$em->flush();
$user->addProject($project);
// TODO Does the JSON Building object include its ID?
return $this->json([
'id' => $building->getId(),
'name' => $building->getName()]
);
}
}

View File

@ -7,6 +7,8 @@ use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\GetCollection;
use App\Repository\BuildingRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Groups;
@ -33,6 +35,17 @@ class Building
#[Groups(['building:list', 'building:item'])]
private ?string $name = null;
/**
* @var Collection<int, Project>
*/
#[ORM\OneToMany(targetEntity: Project::class, mappedBy: 'building')]
private Collection $projects;
public function __construct()
{
$this->projects = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
@ -56,4 +69,34 @@ class Building
return $this;
}
/**
* @return Collection<int, Project>
*/
public function getProjects(): Collection
{
return $this->projects;
}
public function addProject(Project $project): static
{
if (!$this->projects->contains($project)) {
$this->projects->add($project);
$project->setBuilding($this);
}
return $this;
}
public function removeProject(Project $project): static
{
if ($this->projects->removeElement($project)) {
// set the owning side to null (unless already changed)
if ($project->getBuilding() === $this) {
$project->setBuilding(null);
}
}
return $this;
}
}

View File

@ -2,26 +2,12 @@
namespace App\Entity;
use App\Repository\ProjectRepository;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\GetCollection;
use Symfony\Component\Serializer\Attribute\Groups;
use Doctrine\DBAL\Types\Types;
use App\Repository\ProjectRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ProjectRepository::class)]
#[ORM\Table(name: 'progetto')]
#[ApiResource(
operations: [
new Get(normalizationContext: ['groups' => 'project:item']),
new GetCollection(normalizationContext: ['groups' => 'project:list']),
new Post(security: "is_granted('ROLE_USER')"),
],
order: ['name' => 'DESC'],
paginationEnabled: false,
)]
#[ApiResource]
class Project
{
#[ORM\Id]
@ -29,51 +15,48 @@ class Project
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(type: Types::BIGINT, name: 'id_utente')]
#[ORM\OneToOne(User::class, )]
private ?string $userid = null;
#[ORM\ManyToOne(inversedBy: 'projects')]
#[ORM\JoinColumn(nullable: false)]
private ?User $user = null;
#[ORM\Column(type: Types::BIGINT, name: 'id_architettura')]
private ?string $buildingid = null;
#[ORM\ManyToOne(inversedBy: 'projects')]
#[ORM\JoinColumn(nullable: false)]
private ?Building $building = null;
#[ORM\Column(length: 255, nullable: true)]
/**
* @var string $ifc Path to IFC file for this project
*/
private ?string $ifc = null;
#[ORM\Column]
private ?\DateTimeImmutable $createdAt = null;
#[ORM\Column(nullable: true)]
private ?\DateTimeImmutable $lastModified = null;
public function getId(): ?int
{
return $this->id;
}
public function setId(string $id): static
public function getUser(): ?User
{
$this->id = $id;
return $this->user;
}
public function setUser(?User $user): static
{
$this->user = $user;
return $this;
}
public function getUserid(): ?string
public function getBuilding(): ?Building
{
return $this->userid;
return $this->building;
}
public function setUserid(string $userid): static
public function setBuilding(?Building $building): static
{
$this->userid = $userid;
return $this;
}
public function getBuildingid(): ?string
{
return $this->buildingid;
}
public function setBuildingid(string $buildingid): static
{
$this->buildingid = $buildingid;
$this->building = $building;
return $this;
}
@ -89,4 +72,28 @@ class Project
return $this;
}
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeImmutable $createdAt): static
{
$this->createdAt = $createdAt;
return $this;
}
public function getLastModified(): ?\DateTimeImmutable
{
return $this->lastModified;
}
public function setLastModified(?\DateTimeImmutable $lastModified): static
{
$this->lastModified = $lastModified;
return $this;
}
}

View File

@ -3,6 +3,8 @@
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
@ -40,6 +42,17 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
#[ORM\Column]
private ?string $email = null;
/**
* @var Collection<int, Project>
*/
#[ORM\OneToMany(targetEntity: Project::class, mappedBy: 'user')]
private Collection $projects;
public function __construct()
{
$this->projects = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
@ -149,4 +162,34 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
/**
* @return Collection<int, Project>
*/
public function getProjects(): Collection
{
return $this->projects;
}
public function addProject(Project $project): static
{
if (!$this->projects->contains($project)) {
$this->projects->add($project);
$project->setUser($this);
}
return $this;
}
public function removeProject(Project $project): static
{
if ($this->projects->removeElement($project)) {
// set the owning side to null (unless already changed)
if ($project->getUser() === $this) {
$project->setUser(null);
}
}
return $this;
}
}

View File

@ -211,6 +211,15 @@
"ref": "0df5844274d871b37fc3816c57a768ffc60a43a5"
}
},
"symfony/ux-turbo": {
"version": "2.23",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "2.19",
"ref": "9dd2778a116b6e5e01e5e1582d03d5a9e82630de"
}
},
"symfony/validator": {
"version": "7.1",
"recipe": {

View File

@ -0,0 +1,41 @@
{% extends 'base.html.twig' %}
{% block title %}Progetti | WebArchi{% endblock %}
{% block body %}
{% include 'partials/navbar.html.twig' %}
<div class="container" style="max-width: 50vw">
<h1 class="is-size-1 mt-2 has-text-centered">Progetti</h1>
<table class="table is-striped is-fulldwidth m-6 is-hoverable has-text-centered">
<tr><th class="has-text-centered">Architettura</th><th class="has-text-centered">File IFC</th><th class="has-text-centered">Data creazione</th><th class="has-text-centered">Ultima modifica</th><th class="has-text-centered">Azioni</th></tr>
{% for project in projects %}
<tr>
<td>{{ project.building.name }}</td>
<td>{{ project.ifc }}</td>
<td>{{ project.createdAt.format('Y-m-d') }}</td>
<td>{{ project.lastModified.format('Y-m-d H:i:s') }}</td>
<td>
<div class="buttons">
<button class="button is-small is-link" title="Modifica">
<span class="icon">
<i class="bi bi-pencil-fill"></i>
</span>
</button>
<button class="button is-small is-danger" title="Elimina">
<span class="icon">
<i class="bi bi-trash-fill"></i>
</span>
</button>
<button class="button is-small is-primary" title="Condividi">
<span class="icon">
<i class="bi bi-share-fill"></i>
</span>
</button>
</div>
</td>
</tr>
{% endfor %}
</table>
</div>
{% endblock %}

View File

@ -19,7 +19,7 @@
<p class="title is-4">{{ app.user.useridentifier }}</p>
<p class="subtitle is-6">
<span class="icon is-small">
<i class="fa fa-envelope"></i>
<i class="bi bi-envelope"></i>
</span>
{{ app.user.email ?? 'no email' }}
</p>
@ -40,7 +40,7 @@
<div class="message-header">
<p>
<span class="icon is-small is-size-5 mr-3">
<i class="fa fa-info-circle"></i>
<i class="bi bi-info-circle"></i>
</span>
Permessi
</p>
@ -48,12 +48,8 @@
<div class="message-body">
{% if 'ROLE_ADMIN' in app.user.roles %}
<p>
Administrators can create and edit users of the ArCOA data entry system,
including changing passwords and user roles, and disabling accounts.
</p>
<p>
They can perform all actions on all records and vocabularies
(create, view, edit, delete).
Gli amministratori possono creare e modificare utenti in WebArchi,
cambiare password e ruoli utente, e disabilitare account.
</p>
{% elseif 'ROLE_REVISOR' in app.user.roles %}
<p>Revisors can perform all actions (create, view, edit, delete) on all records and vocabs.</p>

View File

@ -0,0 +1,16 @@
<?php
namespace App\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
final class ProjectControllerTest extends WebTestCase
{
public function testIndex(): void
{
$client = static::createClient();
$client->request('GET', '/project');
self::assertResponseIsSuccessful();
}
}