add: CompanyDocument

Add class
* Add CompanyDocument class, associated repo and twig files
This commit is contained in:
Ryan Prather 2025-01-21 14:23:14 -05:00
parent e13dc0bf66
commit 82ee30a724
6 changed files with 496 additions and 0 deletions

View File

@ -0,0 +1,125 @@
<?php
namespace App\Entity;
use App\Enums\DocumentExtras;
use App\Repository\CompanyDocumentRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid;
#[ORM\Entity(repositoryClass: CompanyDocumentRepository::class)]
class CompanyDocument
{
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME, unique: true)]
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
private ?Uuid $id = null;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)]
private ?Company $company = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $text = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
private ?\DateTimeInterface $updated = null;
#[ORM\Column(type: Types::JSON, enumType: DocumentExtras::class, nullable: true)]
private array $extras = [];
public function getId(): ?Uuid
{
return $this->id;
}
public function getCompany(): ?Company
{
return $this->company;
}
public function setCompany(?Company $company): static
{
$this->company = $company;
return $this;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): static
{
$this->title = $title;
return $this;
}
public function getText(): ?string
{
return $this->text;
}
public function setText(?string $text): static
{
$this->text = $text;
return $this;
}
public function getUpdated(): ?\DateTimeInterface
{
return $this->updated;
}
public function setUpdated(\DateTimeInterface $updated): static
{
$this->updated = $updated;
return $this;
}
public function getExtras(): array
{
return $this->extras;
}
public function setExtras(array $extras): static
{
$this->extras = $extras;
return $this;
}
public function inExtras(DocumentExtras $field): bool
{
return in_array($field, $this->extras);
}
public function renderHtml(): string
{
$text = $this->text;
if ($this->inExtras(DocumentExtras::EMAIL)) {
$text = str_replace('{{ email }}', "<input type='email' name='email' id='email' value='{{ doc.client.email }}'/>", $text);
$text = str_replace('{{ checkbox|email }}', "<input type='checkbox' name='emailClient' id='emailClient' value='1'/>", $text);
}
if ($this->inExtras(DocumentExtras::PHONE)) {
$text = str_replace('{{ phone }}', "<input type='tel' name='phone' id='phone' value='{{ doc.client.phone }}'/>", $text);
$text = str_replace('{{ checkbox|phone }}', "<input type='checkbox' name='callClient' id='callClient' value='1'/>", $text);
$text = str_replace('{{ checkbox|text }}', "<input type='checkbox' name='textClient' id='textClient' value='1'/>", $text);
}
return <<<HTML
$text
HTML;
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace App\Repository;
use App\Entity\CompanyDocument;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<CompanyDocument>
*/
class CompanyDocumentRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, CompanyDocument::class);
}
// /**
// * @return CompanyDocument[] Returns an array of CompanyDocument objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('c')
// ->andWhere('c.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('c.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?CompanyDocument
// {
// return $this->createQueryBuilder('c')
// ->andWhere('c.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

View File

@ -0,0 +1,103 @@
{% extends 'base.html.twig' %}
{% block body %}
{{ block('nav', 'internal/libs/nav.html.twig') }}
<main class="main-content position-relative max-height-vh-100 h-100 border-radius-lg ">
{{ block('topnav', 'internal/libs/top-nav.html.twig') }}
<section>
<div class="card card-plain">
<div class="card-header">
<h4 class="font-weight-bolder">Add New Document</h4>
<p class="mb-0"></p>
</div>
<div class="card-body">
<div class="container">
{{ form_errors(form) }}
{{ form_start(form) }}
<div class="row">
<div class='col'>
<div class='input-group input-group-outline mb-3'>
<label for='doc_form_title' class='form-label'>Title</label>
<input type='text' name='{{ field_name(form.title) }}' id='doc_form_title' class='form-control' required='required'/>
</div>
<div class='input-group input-group-outline mb-3'>
{#{{ form_row(form.extras) }}#}
{% for e in enum('App\\Enums\\DocumentExtras').cases() %}
<input type='checkbox' name='{{ field_name(form.extras) }}[]' id='{{ e.name }}' value='{{ e.value }}'/>
<label for='{{ e.name }}' class='extras'>{{ e.name|lower|replace({'_': ' '})|capitalize }}</label>
{% endfor %}
</div>
<div class='input-group input-group-outline mb-3 is-filled'>
<textarea name='{{ field_name(form.text) }}' id='doc-text' placeholder='Document Text'></textarea>
</div>
</div>
</div>
<div class='row'>
<div class="text-center">
<button type="submit" class="btn btn-lg bg-gradient-dark btn-lg w-100 mt-4 mb-0">Save Document</button>
</div>
</div>
{{ form_end(form) }}
</div>
</div>
</div>
</section>
</main>
{% endblock %}
{% block page_js %}
<script src="https://cdn.jsdelivr.net/npm/tinymce@7.6.0/tinymce.min.js"></script>
<script type='module'>
window.onload = function () {
tinymce.init({
selector: '#doc-text',
height: 500,
width: 1200,
plugins: ['advlist',
'autolink',
'lists',
'link',
'image',
'charmap',
'preview',
'anchor',
'searchreplace',
'visualblocks',
'code',
'fullscreen',
'insertdatetime',
'media',
'table',
'help',
'wordcount'/**/
],
toolbar: 'undo redo | ' +
'blocks | ' +
'bold italic underline forecolor | ' +
'alignleft aligncenter alignright | ' +
'bullist numlist outdent indent | ' +
'removeformat | help',
content_style: '' // 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }'
});
}
</script>
{% endblock %}
{% block page_css %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.5/skins/lightgray/content.min.css">
<style type='text/css'>
.extras {
margin: 0 5px !important;
}
#doc-text {
width: 100%;
height: 300px;
}
</style>
{% endblock %}

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<style type='text/css' rel='stylesheet'>
body {
margin: 0;
}
.sheet-outer {
margin: 0;
}
.sheet {
margin: 0;
overflow: hidden;
page-break-after: always;
}
@media screen {
body {
background: #e0e0e0;
}
.sheet {
background: #ffffff;
box-shadow: 0 .5mm 2mm rgba(0,0,0,.3);
margin: 5mm auto;
}
}
@media print {
}
@page {
size: letter;
margin: 0;
}
</style>
</head>
<body>
<header id="header" style='height:100px;'>
<img id="companyLogo" src='{{ company_image_path }}/{{ company.companyLogo }}' style='height:100px;width:100px;'/>
</header>
<main id="body">
<section id='doc-content'></section>
</main>
<footer id='footer'>
</footer>
</body>
</html>

View File

@ -0,0 +1,109 @@
{% extends 'base.html.twig' %}
{% block body %}
{{ block('nav', 'internal/libs/nav.html.twig') }}
<main class="main-content position-relative max-height-vh-100 h-100 border-radius-lg ">
{{ block('topnav', 'internal/libs/top-nav.html.twig') }}
<section>
<div class="card card-plain">
<div class="card-header">
<h4 class="font-weight-bolder">Edit Document</h4>
<p class="mb-0"></p>
</div>
<div class="card-body">
<div class="container">
{{ form_errors(form) }}
{{ form_start(form) }}
<div class="row">
<div class='col'>
<div class='input-group input-group-outline mb-3 is-filled'>
<label for='doc_form_title' class='form-label'>Title</label>
<input type='text' name='{{ field_name(form.title) }}' value='{{ doc.title }}' id='doc_form_title' class='form-control' required='required'/>
</div>
<div class='input-group input-group-outline mb-3'>
{{ form_row(form.extras) }}
{# for e in enum('App\\Enums\\DocumentExtras').cases %}
<input type='checkbox' name='{{ field_name(form.extras) }}[]' id='{{ e.name }}' value='{{ e.value }}'/>
<label for='{{ e.name }}' class='extras'>{{ e.name|lower|replace({'_': ' '})|capitalize }}</label>
{% endfor #}
</div>
<div class='input-group input-group-outline mb-3 is-filled'>
<textarea name='{{ field_name(form.text) }}' id='doc-text' placeholder='Document Text'></textarea>
</div>
</div>
</div>
<div class='row'>
<div class="text-center">
<button type="submit" class="btn btn-lg bg-gradient-dark btn-lg w-100 mt-4 mb-0">Save Document</button>
</div>
</div>
{{ form_end(form) }}
</div>
</div>
</div>
</section>
</main>
{% endblock %}
{% block page_js %}
<script src="https://cdn.jsdelivr.net/npm/tinymce@7.6.0/tinymce.min.js"></script>
<script type='module'>
window.onload = function () {
tinymce.init({
license_key: 'gpl',
selector: '#doc-text',
height: 500,
width: 1200,
plugins: ['advlist',
'autolink',
'lists',
'link',
'image',
'charmap',
'preview',
'anchor',
'searchreplace',
'visualblocks',
'code',
'fullscreen',
'insertdatetime',
'media',
'table',
'help',
'wordcount'/**/
],
toolbar: 'undo redo | ' +
'blocks | ' +
'bold italic underline forecolor | ' +
'alignleft aligncenter alignright | ' +
'bullist numlist outdent indent | ' +
'removeformat | help',
content_style: '',
setup: function(editor) {
editor.on('init', function(e) {
editor.setContent("{{ docText|raw }}");
});
}
});
}
</script>
{% endblock %}
{% block page_css %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.5/skins/lightgray/content.min.css">
<style type='text/css'>
.extras {
margin: 0 5px !important;
}
#doc-text {
width: 100%;
height: 300px;
}
</style>
{% endblock %}

View File

@ -0,0 +1,66 @@
{% extends 'base.html.twig' %}
{% block body %}
{{ block('nav', 'internal/libs/nav.html.twig') }}
<main class="main-content position-relative max-height-vh-100 h-100 border-radius-lg ">
{{ block('topnav', 'internal/libs/top-nav.html.twig') }}
<div class="container-fluid py-2">
<div class="row">
<div class="col-12">
<div class="card my-4">
<div class="card-header p-0 position-relative mt-n4 mx-3 z-index-2">
<div class="d-flex justify-content-between bg-gradient-dark shadow-dark border-radius-lg pt-4 pb-3 ps-3 p-2">
<div>
<h6 class="text-white text-capitalize ps-3">Document List</h6>
</div>
{% if is_granted('ROLE_ADMIN') %}
<div>
<button type="button" class="btn btn-block btn-light mb-3" onclick="window.open('{{ path('app_add_doc') }}', '_self')">Add Document</button>
</div>
{% endif %}
</div>
</div>
<div class="card-body px-0 pb-2">
<div class="table-responsive p-0">
<table class="table align-items-center mb-0">
<thead>
<tr>
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7">Title</th>
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2">Last Update</th>
<th class="text-secondary opacity-7"></th>
</tr>
</thead>
<tbody id='doc-list'>
{% for d in docs %}
<tr>
<td>
<div class='d-flex px-2 py-1'>
<div class='d-flex flex-column justify-content-center'>
<h6 class='mb-0 text-small'>{{ d.title }}</h6>
</div>
</div>
</td>
<td>
<p class='text-xs font-weight-bold mb-0'>{{ d.updated|date('F j, Y', company_timezone) }}</p>
</td>
<td class='align-right'>
{% if is_granted('ROLE_ADMIN') %}
<a href='{{ path('app_edit_doc', {docId: d.id}) }}' class='' title='Edit Document' data-toggle='tooltip'>
<i class="material-symbols-rounded opacity-5">edit</i>
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
{% endblock %}