✨ feat(CustomerController): Ajoute route pour création de commande client
Ajoute une route pour créer des commandes (devis, avis de paiement, facture) pour un client spécifique. Crée template associé.
This commit is contained in:
3
.env
3
.env
@@ -57,3 +57,6 @@ INSEE_KEYAPI=f941bdf0-b8a3-4e4c-81bd-f0b8a35e4cc8
|
||||
OVH_KEY=34bc2c2eb416b67d
|
||||
OVH_SECRET=12239d273975b5ab53318907fb66d355
|
||||
OVH_CUSTOMER=56c387eb9ca4b9a2de4d4d97fd3d7f22
|
||||
|
||||
DOCUSIGN_URL=signature.esy-web.dev
|
||||
DOCUSIGN_KEY=52u82oCoiG79awGsuxLfJqhxYjg8mrJfAsJJHejRMFa
|
||||
|
||||
@@ -14,7 +14,7 @@ label,span,input,{
|
||||
color:var(--color-red-700);
|
||||
}
|
||||
|
||||
|
||||
select,
|
||||
input {
|
||||
background: oklch(21% 0.034 264.665);
|
||||
color: white;
|
||||
|
||||
133
assets/libs/repeaterComponent.js
Normal file
133
assets/libs/repeaterComponent.js
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Repeater Component - Duplicates the first element in its `rows` ref up to `maxRows` times.
|
||||
*
|
||||
* Suggested structure (Emmet):
|
||||
* [data-component="repeater"]>(ol[data-ref="rows"][tabindex="0"]>li>((fieldset.your-fields>input)+button[data-ref="removeButton"]))+button[data-ref="addButton"]
|
||||
*
|
||||
* Refs:
|
||||
* {HTMLElement | HTMLOListElement | HTMLUListElement} rows - The element that holds the repeated fields. Should be focusable.
|
||||
* {HTMLButtonElement} removeButton - Button for removing a row. Place inside the default row.
|
||||
* {HTMLButtonElement} addButton - Button for adding a row.
|
||||
*
|
||||
* Props:
|
||||
* {?number} maxRows - The most rows the repeater will let the user add.
|
||||
*/
|
||||
export function repeaterComponent($el) {
|
||||
const $props = getProps($el, { maxRows: 5 });
|
||||
const $refs = getRefs($el);
|
||||
|
||||
let rowHTML = $refs.rows.children[0].outerHTML;
|
||||
|
||||
// Hook up events for the row.
|
||||
function setUpRow(row) {
|
||||
const rowRefs = getRefs(row);
|
||||
|
||||
rowRefs.removeButton.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
removeRow(row);
|
||||
};
|
||||
}
|
||||
|
||||
// Enable or disable addButton as necessary.
|
||||
function updateAddButton() {
|
||||
if ($refs.rows.children.length >= $props.maxRows) {
|
||||
$refs.addButton.setAttribute('disabled', '');
|
||||
return;
|
||||
}
|
||||
|
||||
$refs.addButton.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
// Update array key values to the row number
|
||||
function updateFieldNames() {
|
||||
[...$refs.rows.children]
|
||||
.forEach((el, index) => {
|
||||
el.querySelectorAll('[name]')
|
||||
.forEach(el => {
|
||||
const newName = el.getAttribute('name').replace(/\[\d\]/gm, `[${index}]`);
|
||||
|
||||
el.setAttribute('name', newName);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addRow() {
|
||||
if (
|
||||
!rowHTML ||
|
||||
$refs.rows.children.length >= $props.maxRows
|
||||
) return;
|
||||
|
||||
let newRow = createFromHTML(rowHTML);
|
||||
newRow.removeAttribute('id');
|
||||
setUpRow(newRow);
|
||||
|
||||
$refs.rows.appendChild(newRow);
|
||||
newRow.querySelector('input,textarea,select').focus();
|
||||
|
||||
updateFieldNames();
|
||||
updateAddButton();
|
||||
}
|
||||
|
||||
function removeRow(row) {
|
||||
if ($refs.rows.children.length <= 1) return;
|
||||
|
||||
row.remove();
|
||||
$refs.rows.focus();
|
||||
|
||||
updateFieldNames();
|
||||
updateAddButton();
|
||||
}
|
||||
|
||||
function init() {
|
||||
setUpRow($refs.rows.children[0]);
|
||||
|
||||
$refs.addButton.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
addRow();
|
||||
}
|
||||
|
||||
updateFieldNames();
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
// Return an object that contains references to DOM objects.
|
||||
function getRefs(el) {
|
||||
let result = {};
|
||||
|
||||
[...el.querySelectorAll('[data-ref]')]
|
||||
.forEach(ref => {
|
||||
result[ref.dataset.ref] = ref;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function setDefaults(obj, defaults) {
|
||||
let results = obj;
|
||||
|
||||
for (const prop in defaults) {
|
||||
if (!obj.hasOwnProperty(prop)) {
|
||||
results[prop] = defaults[prop];
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// Get initial component data from the `data-props` attribute in JSON format.
|
||||
function getProps(el, defaults={}) {
|
||||
return setDefaults(
|
||||
JSON.parse(el.dataset.props ?? '{}'),
|
||||
defaults
|
||||
);
|
||||
}
|
||||
|
||||
// Create a new element from an HTML string.
|
||||
function createFromHTML(html='') {
|
||||
let element = document.createElement(null);
|
||||
element.innerHTML = html;
|
||||
return element.firstElementChild;
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
"@sentry/browser": "^9.34.0",
|
||||
"@tailwindcss/vite": "^4.1.10",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"sortablejs": "^1.15.6",
|
||||
"tailwindcss": "^4.1.10"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,4 +169,12 @@ class CustomerController extends AbstractController
|
||||
'form' => $form->createView()
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route(path: '/artemis/intranet/customer/{id}/orderAdd',name: 'artemis_intranet_customer_orderAdd',methods: ['GET', 'POST'])]
|
||||
public function customerOrder(?Customer $customer,Request $request,LoggerService $loggerService,EntityManagerInterface $entityManager,EventDispatcherInterface $eventDispatcher): Response
|
||||
{
|
||||
return $this->render('artemis/intranet/customer/order-add.twig',[
|
||||
'customer' => $customer,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
9
templates/artemis/intranet/customer/order-add.twig
Normal file
9
templates/artemis/intranet/customer/order-add.twig
Normal file
@@ -0,0 +1,9 @@
|
||||
{% extends 'artemis/base.twig' %}
|
||||
{% block title %}Intranet - Client - {{ customer.raisonSocial }} - Création devis / avis de paiement / facture{% endblock %}
|
||||
{% block content %}
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-3xl font-semibold text-gray-800 dark:text-gray-200">Client - {{ customer.raisonSocial }} - Création devis / avis de paiement / facture</h2>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,16 +1,75 @@
|
||||
<div class="flex space-x-4 mb-6 border-b border-gray-700">
|
||||
{% set active = "text-sm border-b-2 border-purple-500" %}
|
||||
{% set desactive = "text-sm text-gray-400 hover:text-white" %}
|
||||
<a href="{{ path('artemis_intranet_customer_view',{id:customer.id,current:'order',currentOrder:'f'}) }}" class="px-4 py-2 font-semibold {% if currentOrder == "f" %}{{ active }}{% else %}{{ desactive }}{% endif %}">
|
||||
<i class="fad fa-home"></i>
|
||||
Facture
|
||||
</a>
|
||||
<a href="{{ path('artemis_intranet_customer_view',{id:customer.id,current:'order',currentOrder:'a'}) }}" class="px-4 py-2 font-semibold {% if currentOrder == "a" %}{{ active }}{% else %}{{ desactive }}{% endif %}">
|
||||
<i class="fad fa-file-pdf"></i>
|
||||
Avis de paiement
|
||||
</a>
|
||||
<a href="{{ path('artemis_intranet_customer_view',{id:customer.id,current:'order',currentOrder:'d'}) }}" class="px-4 py-2 font-semibold {% if currentOrder == "d" %}{{ active }}{% else %}{{ desactive }}{% endif %}">
|
||||
<i class="fad fa-spider-web"></i>
|
||||
Devis
|
||||
</a>
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-3xl font-semibold text-gray-800 dark:text-gray-200">Factures / Devis / Avis de Paiement</h2>
|
||||
<div>
|
||||
<a href="{{ path('artemis_intranet_customer_orderAdd',{id:customer.id}) }}" class="px-4 py-2 bg-blue-600 text-white font-medium rounded-md shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900">
|
||||
+ Crée un devis / avis de paiement / facture
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Barre de recherche + filtre type -->
|
||||
<div class="bg-gray-800 rounded-lg shadow p-6 mb-6">
|
||||
<form method="get" action="" class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label for="searchInput" class="block mb-1 font-semibold">Rechercher :</label>
|
||||
<input
|
||||
type="search"
|
||||
id="searchInput"
|
||||
name="search"
|
||||
placeholder="Nom ou numéro de document"
|
||||
class="w-full px-4 py-2 rounded-md text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="typeFilter" class="block mb-1 font-semibold">Filtrer par type :</label>
|
||||
<select
|
||||
id="typeFilter"
|
||||
name="type"
|
||||
class="w-full px-4 py-2 rounded-md text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">Tous</option>
|
||||
<option value="facture">Facture</option>
|
||||
<option value="devis">Devis</option>
|
||||
<option value="avis">Avis de paiement</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<button
|
||||
type="submit"
|
||||
class="mt-3 bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded"
|
||||
>
|
||||
Filtrer
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Tableau -->
|
||||
<div class="overflow-x-auto bg-gray-800 rounded-lg shadow">
|
||||
<table class="min-w-full divide-y divide-gray-700">
|
||||
<thead class="bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Type</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">N°</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Date</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Montant</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Statut</th>
|
||||
<th class="px-6 py-3 text-center text-xs font-medium uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="hover:bg-gray-700">
|
||||
<td class="px-6 py-4">Facture</td>
|
||||
<td class="px-6 py-4">F2025-001</td>
|
||||
<td class="px-6 py-4">2025-07-01</td>
|
||||
<td class="px-6 py-4">1 200,00 €</td>
|
||||
<td class="px-6 py-4 text-green-400">Payé</td>
|
||||
<td class="px-6 py-4 text-center">
|
||||
<button class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded">Modifier</button>
|
||||
<button class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded">Télécharger</button>
|
||||
<button class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded">Annulée</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user