added customer administration
This commit is contained in:
111
src/TimesheetBundle/Controller/Admin/CustomerController.php
Normal file
111
src/TimesheetBundle/Controller/Admin/CustomerController.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Controller\Admin;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use TimesheetBundle\Entity\Customer;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
use TimesheetBundle\Form\CustomerEditForm;
|
||||
use TimesheetBundle\Repository\CustomerRepository;
|
||||
|
||||
/**
|
||||
* Controller used to manage activities in the admin part of the site.
|
||||
*
|
||||
* @Route("/admin/customer")
|
||||
* @Security("has_role('ROLE_ADMIN')")
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class CustomerController extends Controller
|
||||
{
|
||||
/**
|
||||
* @Route("/", defaults={"page": 1}, name="admin_customer")
|
||||
* @Route("/page/{page}", requirements={"page": "[1-9]\d*"}, name="admin_customer_paginated")
|
||||
* @Method("GET")
|
||||
* @Cache(smaxage="10")
|
||||
*/
|
||||
public function indexAction($page)
|
||||
{
|
||||
/* @var $entries Pagerfanta */
|
||||
$entries = $this->getDoctrine()->getRepository(Customer::class)->findAll($page);
|
||||
|
||||
return $this->render('TimesheetBundle:admin:customer.html.twig', ['entries' => $entries]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{id}/edit", name="admin_customer_edit")
|
||||
* @Method({"GET", "POST"})
|
||||
*/
|
||||
public function editAction($id, Request $request)
|
||||
{
|
||||
$entity = $this->getById($id);
|
||||
$editForm = $this->createEditForm($entity);
|
||||
|
||||
$editForm->handleRequest($request);
|
||||
|
||||
if ($editForm->isSubmitted() && $editForm->isValid()) {
|
||||
$entityManager = $this->getDoctrine()->getManager();
|
||||
$entityManager->persist($entity);
|
||||
$entityManager->flush();
|
||||
|
||||
$this->addFlash('success', 'action.updated_successfully');
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'admin_customer', ['id' => $entity->getId()]
|
||||
);
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
'TimesheetBundle:admin:customer_edit.html.twig',
|
||||
[
|
||||
'customer' => $entity,
|
||||
'form' => $editForm->createView()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @return null|Customer
|
||||
*/
|
||||
protected function getById($id)
|
||||
{
|
||||
/* @var $repo CustomerRepository */
|
||||
$repo = $this->getDoctrine()->getRepository(Customer::class);
|
||||
$activity = $repo->getById($id);
|
||||
if (null === $activity) {
|
||||
throw new NotFoundHttpException('Customer "'.$id.'" does not exist');
|
||||
}
|
||||
return $activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Customer $customer
|
||||
* @return \Symfony\Component\Form\Form
|
||||
*/
|
||||
private function createEditForm(Customer $customer)
|
||||
{
|
||||
return $this->createForm(
|
||||
CustomerEditForm::class,
|
||||
$customer,
|
||||
[
|
||||
'action' => $this->generateUrl('admin_customer_edit', ['id' => $customer->getId()]),
|
||||
'method' => 'POST'
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,13 @@
|
||||
namespace TimesheetBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
* Customer
|
||||
*
|
||||
* @ORM\Table(name="customers")
|
||||
* @ORM\Entity
|
||||
* @ORM\Entity(repositoryClass="TimesheetBundle\Repository\CustomerRepository")
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
@@ -69,9 +70,11 @@ class Customer
|
||||
private $company;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var integer
|
||||
*
|
||||
* @ORM\Column(name="vat", type="integer", length=2, nullable=true)
|
||||
* @Assert\Range(min = 0, max = 99)
|
||||
*
|
||||
* @ORM\Column(name="vat", type="string", length=255, nullable=true)
|
||||
*/
|
||||
private $vat;
|
||||
|
||||
@@ -85,28 +88,15 @@ class Customer
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="street", type="string", length=255, nullable=true)
|
||||
* @ORM\Column(name="street", type="text", length=65535, nullable=true)
|
||||
*/
|
||||
private $street;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="zipcode", type="string", length=255, nullable=true)
|
||||
*/
|
||||
private $zipcode;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="city", type="string", length=255, nullable=true)
|
||||
*/
|
||||
private $city;
|
||||
private $address;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="country", type="string", length=2, nullable=true)
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
private $country;
|
||||
|
||||
@@ -149,6 +139,7 @@ class Customer
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="timezone", type="string", length=255, nullable=false)
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
private $timezone;
|
||||
|
||||
@@ -259,7 +250,7 @@ class Customer
|
||||
/**
|
||||
* Set vat
|
||||
*
|
||||
* @param string $vat
|
||||
* @param integer $vat
|
||||
*
|
||||
* @return Customer
|
||||
*/
|
||||
@@ -273,7 +264,7 @@ class Customer
|
||||
/**
|
||||
* Get vat
|
||||
*
|
||||
* @return string
|
||||
* @return integer
|
||||
*/
|
||||
public function getVat()
|
||||
{
|
||||
@@ -305,77 +296,25 @@ class Customer
|
||||
}
|
||||
|
||||
/**
|
||||
* Set street
|
||||
*
|
||||
* @param string $street
|
||||
* @return string
|
||||
*/
|
||||
public function getAddress()
|
||||
{
|
||||
return $this->address;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $address
|
||||
*
|
||||
* @return Customer
|
||||
*/
|
||||
public function setStreet($street)
|
||||
public function setAddress($address)
|
||||
{
|
||||
$this->street = $street;
|
||||
$this->address = $address;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get street
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStreet()
|
||||
{
|
||||
return $this->street;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set zipcode
|
||||
*
|
||||
* @param string $zipcode
|
||||
*
|
||||
* @return Customer
|
||||
*/
|
||||
public function setZipcode($zipcode)
|
||||
{
|
||||
$this->zipcode = $zipcode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get zipcode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getZipcode()
|
||||
{
|
||||
return $this->zipcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set city
|
||||
*
|
||||
* @param string $city
|
||||
*
|
||||
* @return Customer
|
||||
*/
|
||||
public function setCity($city)
|
||||
{
|
||||
$this->city = $city;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get city
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCity()
|
||||
{
|
||||
return $this->city;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set country
|
||||
*
|
||||
@@ -543,4 +482,31 @@ class Customer
|
||||
{
|
||||
return $this->timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Project[] $projects
|
||||
* @return $this
|
||||
*/
|
||||
public function setProjects($projects)
|
||||
{
|
||||
$this->projects = $projects;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Project[]
|
||||
*/
|
||||
public function getProjects()
|
||||
{
|
||||
return $this->projects;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ class Menu
|
||||
|
||||
$menu->addChild(
|
||||
new MenuItemModel('timesheet_admin', 'menu.admin_timesheet', 'admin_timesheet', [], 'fa fa-clock-o')
|
||||
)->addChild(
|
||||
new MenuItemModel('customer_admin', 'menu.admin_customer', 'admin_customer', [], 'fa fa-users')
|
||||
)->addChild(
|
||||
new MenuItemModel('project_admin', 'menu.admin_project', 'admin_project', [], 'fa fa-book')
|
||||
)->addChild(
|
||||
|
||||
134
src/TimesheetBundle/Form/CustomerEditForm.php
Normal file
134
src/TimesheetBundle/Form/CustomerEditForm.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Form;
|
||||
|
||||
use AppBundle\Form\Type\YesNoType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CountryType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\PercentType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TelType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TimezoneType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\UrlType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use TimesheetBundle\Entity\Customer;
|
||||
|
||||
/**
|
||||
* Defines the form used to edit Customer entities.
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class CustomerEditForm extends AbstractType
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
// string - length 255
|
||||
->add('name', TextType::class, [
|
||||
'label' => 'label.name',
|
||||
])
|
||||
// text
|
||||
->add('comment', TextareaType::class, [
|
||||
'label' => 'label.comment',
|
||||
'required' => false,
|
||||
])
|
||||
// do not allow project selection:
|
||||
// 1. it is a bad UX
|
||||
// 2. what should happen if they are detached?
|
||||
/*
|
||||
->add('projects', EntityType::class, [
|
||||
'label' => 'label.project',
|
||||
'class' => 'TimesheetBundle:Project',
|
||||
'multiple' => true,
|
||||
'expanded' => true
|
||||
])
|
||||
*/
|
||||
// boolean
|
||||
->add('visible', YesNoType::class, [
|
||||
'label' => 'label.visible',
|
||||
])
|
||||
// string - length 255
|
||||
->add('company', TextType::class, [
|
||||
'label' => 'label.company',
|
||||
'required' => false,
|
||||
])
|
||||
// string - length 255
|
||||
->add('vat', PercentType::class, [
|
||||
'label' => 'label.vat',
|
||||
'type' => 'integer',
|
||||
])
|
||||
// string - length 255
|
||||
->add('contact', TextType::class, [
|
||||
'label' => 'label.contact',
|
||||
'required' => false,
|
||||
])
|
||||
// string - length 255
|
||||
->add('address', TextareaType::class, [
|
||||
'label' => 'label.address',
|
||||
'required' => false,
|
||||
])
|
||||
// TODO string - length 2
|
||||
->add('country', CountryType::class, [
|
||||
'label' => 'label.country',
|
||||
])
|
||||
// string - length 255
|
||||
->add('phone', TelType::class, [
|
||||
'label' => 'label.phone',
|
||||
'required' => false,
|
||||
])
|
||||
// string - length 255
|
||||
->add('fax', TelType::class, [
|
||||
'label' => 'label.fax',
|
||||
'required' => false,
|
||||
])
|
||||
// string - length 255
|
||||
->add('mobile', TelType::class, [
|
||||
'label' => 'label.mobile',
|
||||
'required' => false,
|
||||
])
|
||||
// string - length 255
|
||||
->add('mail', EmailType::class, [
|
||||
'label' => 'label.email',
|
||||
'required' => false,
|
||||
])
|
||||
// string - length 255
|
||||
->add('homepage', UrlType::class, [
|
||||
'label' => 'label.homepage',
|
||||
'required' => false,
|
||||
])
|
||||
// string - length 255
|
||||
->add('timezone', TimezoneType::class, [
|
||||
'label' => 'label.timezone',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => Customer::class,
|
||||
'csrf_protection' => true,
|
||||
'csrf_field_name' => '_token',
|
||||
'csrf_token_id' => 'admin_activity_edit',
|
||||
]);
|
||||
}
|
||||
}
|
||||
41
src/TimesheetBundle/Model/CustomerStatistic.php
Normal file
41
src/TimesheetBundle/Model/CustomerStatistic.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Model;
|
||||
|
||||
/**
|
||||
* Customer statistics
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class CustomerStatistic
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $totalAmount = 0;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalAmount()
|
||||
{
|
||||
return $this->totalAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $totalAmount
|
||||
*/
|
||||
public function setTotalAmount($totalAmount)
|
||||
{
|
||||
$this->totalAmount = $totalAmount;
|
||||
}
|
||||
}
|
||||
69
src/TimesheetBundle/Repository/AbstractRepository.php
Normal file
69
src/TimesheetBundle/Repository/AbstractRepository.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Repository;
|
||||
|
||||
use AppBundle\Entity\User;
|
||||
use TimesheetBundle\Entity\Activity;
|
||||
use TimesheetBundle\Entity\Timesheet;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query;
|
||||
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use TimesheetBundle\Model\ActivityStatistic;
|
||||
|
||||
/**
|
||||
* Class AbstractRepository
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
abstract class AbstractRepository extends EntityRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $orderBy
|
||||
* @return Query
|
||||
*/
|
||||
protected function queryAll($orderBy = 'id')
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->select('a')
|
||||
->from($this->getEntityName(), 'a')
|
||||
->orderBy('a.' . $orderBy, 'ASC');
|
||||
|
||||
return $qb->getQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $page
|
||||
*
|
||||
* @return Pagerfanta
|
||||
*/
|
||||
public function findAll($page = 1)
|
||||
{
|
||||
return $this->getPager($this->queryAll(), $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Query $query
|
||||
* @param int $page
|
||||
* @return Pagerfanta
|
||||
*/
|
||||
protected function getPager(Query $query, $page = 1)
|
||||
{
|
||||
$paginator = new Pagerfanta(new DoctrineORMAdapter($query, false));
|
||||
$paginator->setMaxPerPage(25);
|
||||
$paginator->setCurrentPage($page);
|
||||
|
||||
return $paginator;
|
||||
}
|
||||
}
|
||||
50
src/TimesheetBundle/Repository/CustomerRepository.php
Normal file
50
src/TimesheetBundle/Repository/CustomerRepository.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Repository;
|
||||
|
||||
use TimesheetBundle\Entity\Customer;
|
||||
use TimesheetBundle\Model\CustomerStatistic;
|
||||
|
||||
/**
|
||||
* Class CustomerRepository
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class CustomerRepository extends AbstractRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @return null|Customer
|
||||
*/
|
||||
public function getById($id)
|
||||
{
|
||||
return $this->find($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return statistic data for all customer.
|
||||
*
|
||||
* @return CustomerStatistic
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getGlobalStatistics()
|
||||
{
|
||||
$countAll = $this->getEntityManager()
|
||||
->createQuery('SELECT COUNT(a.id) FROM TimesheetBundle:Customer c')
|
||||
->getSingleScalarResult();
|
||||
|
||||
$stats = new CustomerStatistic();
|
||||
$stats->setTotalAmount($countAll);
|
||||
return $stats;
|
||||
}
|
||||
}
|
||||
60
src/TimesheetBundle/Resources/views/admin/customer.html.twig
Normal file
60
src/TimesheetBundle/Resources/views/admin/customer.html.twig
Normal file
@@ -0,0 +1,60 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% import "macros/datatables.html.twig" as tables %}
|
||||
|
||||
{% block page_title %}{{ 'admin_customer.title'|trans }}{% endblock %}
|
||||
{% block page_subtitle %}{{ 'admin_customer.subtitle'|trans }} {{ 'subtitle.amount'|trans({'%count%': entries.count}) }}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{{ include('default/_flash_messages.html.twig') }}
|
||||
|
||||
{#
|
||||
private $vat;
|
||||
private $contact;
|
||||
private $street;
|
||||
private $zipcode;
|
||||
private $city;
|
||||
private $country;
|
||||
private $phone;
|
||||
private $fax;
|
||||
private $mobile;
|
||||
private $mail;
|
||||
private $homepage;
|
||||
private $timezone;
|
||||
#}
|
||||
|
||||
{% if entries.count > 0 %}
|
||||
{{ tables.data_table_header({
|
||||
'label.id': 'hidden-xs',
|
||||
'label.name': '',
|
||||
'label.project': '',
|
||||
'label.comment': 'hidden-xs',
|
||||
'label.visible': '',
|
||||
'label.actions': '',
|
||||
}) }}
|
||||
|
||||
{% for entry in entries %}
|
||||
<tr>
|
||||
<td class="hidden-xs">{{ entry.id }}</td>
|
||||
<td>{{ entry.name }} {% if entry.company is not empty %}({{ entry.company }}){% endif %}</td>
|
||||
<td>
|
||||
{% for project in entry.projects %}
|
||||
<a href="{{ path('admin_project_edit', {'id' : project.id}) }}">{{ widgets.label_project(project) }}</a>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="hidden-xs">{{ entry.comment }}</td>
|
||||
<td>{{ widgets.label_visible(entry.visible) }}</td>
|
||||
<td>
|
||||
{{ widgets.button_group({
|
||||
'edit': path('admin_customer_edit', {'id': entry.id}),
|
||||
'trash': '#'
|
||||
}) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
{{ tables.data_table_footer(entries, 'admin_customer_paginated') }}
|
||||
{% else %}
|
||||
{{ widgets.callout('warning', 'error.no_entries_found') }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% import "macros/datatables.html.twig" as tables %}
|
||||
|
||||
{% block page_title %}{{ 'admin_customer.title'|trans }}{% endblock %}
|
||||
{% block page_subtitle %}{{ 'admin_customer.subtitle'|trans }}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{{ include('default/_flash_messages.html.twig') }}
|
||||
{{ include('default/_form.html.twig', {
|
||||
'title': customer.name,
|
||||
'form': form,
|
||||
'back': path('admin_customer')
|
||||
}) }}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user