progress in site admin dashboard

This commit is contained in:
Hesabix 2025-04-06 14:20:29 +00:00
parent 335166d839
commit 0111427a09
8 changed files with 115 additions and 126 deletions

24
package-lock.json generated
View file

@ -2123,8 +2123,14 @@
} }
}, },
"node_modules/@symfony/ux-turbo": { "node_modules/@symfony/ux-turbo": {
"resolved": "vendor/symfony/ux-turbo/assets", "version": "0.1.0",
"link": true "resolved": "file:vendor/symfony/ux-turbo/assets",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@hotwired/stimulus": "^3.0.0",
"@hotwired/turbo": "^7.1.1 || ^8.0"
}
}, },
"node_modules/@symfony/webpack-encore": { "node_modules/@symfony/webpack-encore": {
"version": "5.0.1", "version": "5.0.1",
@ -6875,20 +6881,6 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
},
"vendor/symfony/ux-turbo/assets": {
"name": "@symfony/ux-turbo",
"version": "0.1.0",
"dev": true,
"license": "MIT",
"devDependencies": {
"@hotwired/stimulus": "^3.0.0",
"@hotwired/turbo": "^7.1.0 || ^8.0"
},
"peerDependencies": {
"@hotwired/stimulus": "^3.0.0",
"@hotwired/turbo": "^7.1.1 || ^8.0"
}
} }
} }
} }

38
public/css/login.css Normal file
View file

@ -0,0 +1,38 @@
/* public/css/login.css */
body {
direction: rtl;
text-align: right;
font-family: 'Vazir', 'Tahoma', sans-serif; /* فونت فارسی دلخواه */
}
.login-wrapper {
direction: rtl;
}
.form-group label {
text-align: right;
}
.form-control {
direction: rtl;
text-align: right;
}
.btn {
direction: rtl;
}
.checkbox label {
padding-right: 25px; /* فاصله برای چک‌باکس */
padding-left: 0;
}
.login-box {
margin-right: auto;
margin-left: auto;
}
/* تنظیمات اضافی برای المان‌های خاص */
.login-box-header h1 {
text-align: right;
}

View file

@ -39,39 +39,16 @@ class DashboardController extends AbstractDashboardController
public function configureDashboard(): Dashboard public function configureDashboard(): Dashboard
{ {
return Dashboard::new() return Dashboard::new()
// you can include HTML contents too (e.g. to link to an image)
->setTitle('پیشخوان') ->setTitle('پیشخوان')
// by default EasyAdmin displays a black square as its default favicon;
// use this method to display a custom favicon: the given path is passed
// "as is" to the Twig asset() function:
// <link rel="shortcut icon" href="{{ asset('...') }}">
->setFaviconPath('favicon/favicon.ico') ->setFaviconPath('favicon/favicon.ico')
// the domain used by default is 'messages'
->setTranslationDomain('admin') ->setTranslationDomain('admin')
// set this option if you prefer the page content to span the entire
// browser width, instead of the default design which sets a max width
->renderContentMaximized() ->renderContentMaximized()
// by default, the UI color scheme is 'auto', which means that the backend
// will use the same mode (light/dark) as the operating system and will
// change in sync when the OS mode changes.
// Use this option to set which mode ('light', 'dark' or 'auto') will users see
// by default in the backend (users can change it via the color scheme selector)
->setDefaultColorScheme('dark') ->setDefaultColorScheme('dark')
// instead of magic strings, you can use constants as the value of
// this option: EasyCorp\Bundle\EasyAdminBundle\Config\Option\ColorScheme::DARK
// by default, all backend URLs are generated as absolute URLs. If you
// need to generate relative URLs instead, call this method
->generateRelativeUrls() ->generateRelativeUrls()
->setLocales([
->setLocales(['en','fa']) 'fa' => Locale::new('fa', 'فارسی', 'fa_IR'), // زبان پیش‌فرض
// to further customize the locale option, pass an instance of 'en' => Locale::new('en', 'English', 'en_US'),
// EasyCorp\Bundle\EasyAdminBundle\Config\Locale ]);
;
} }
public function configureMenuItems(): iterable public function configureMenuItems(): iterable

View file

@ -5,15 +5,14 @@ namespace App\Controller\Admin;
use App\Entity\Cat; use App\Entity\Cat;
use App\Entity\Post; use App\Entity\Post;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud; use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Config\Filters;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController; use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField; use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
use EasyCorp\Bundle\EasyAdminBundle\Field\BooleanField;
use EasyCorp\Bundle\EasyAdminBundle\Field\CodeEditorField;
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField; use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
use EasyCorp\Bundle\EasyAdminBundle\Filter\EntityFilter;
class PostCrudController extends AbstractCrudController class PostCrudController extends AbstractCrudController
{ {
@ -32,21 +31,25 @@ class PostCrudController extends AbstractCrudController
TextField::new('title', 'عنوان'), TextField::new('title', 'عنوان'),
TextareaField::new('intro', 'خلاصه مطلب')->hideOnIndex(), TextareaField::new('intro', 'خلاصه مطلب')->hideOnIndex(),
TextEditorField::new('body', 'متن')->hideOnIndex(), TextEditorField::new('body', 'متن')->hideOnIndex(),
CodeEditorField::new('plain', 'ساختار')->hideOnIndex(),
TextField::new('keywords', 'کلیدواژه‌ها'), TextField::new('keywords', 'کلیدواژه‌ها'),
ImageField::new('mainPic', 'تصویر شاخص') ImageField::new('mainPic', 'تصویر شاخص')
->setUploadDir('/public/uploaded/') ->setUploadDir('/public/uploaded/')
->setBasePath('/uploaded/') ->setBasePath('/uploaded/'),
]; ];
} }
public function configureCrud(Crud $crud): Crud public function configureCrud(Crud $crud): Crud
{ {
return $crud return $crud
// the labels used to refer to this entity in titles, buttons, etc.
->setEntityLabelInSingular('محتوا') ->setEntityLabelInSingular('محتوا')
->setEntityLabelInPlural('محتواها') ->setEntityLabelInPlural('محتواها')
; ->setDefaultSort(['dateSubmit' => 'DESC']); // مرتب‌سازی پیش‌فرض بر اساس تاریخ ارسال (جدیدترین)
}
public function configureFilters(Filters $filters): Filters
{
return $filters
->add(EntityFilter::new('cat', 'نوع محتوا')); // فیلتر برای نوع محتوا
} }
public function createEntity(string $entityFqcn) public function createEntity(string $entityFqcn)
@ -57,5 +60,4 @@ class PostCrudController extends AbstractCrudController
$item->setViews(0); $item->setViews(0);
return $item; return $item;
} }
} }

View file

@ -9,6 +9,8 @@ use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\HttpFoundation\Request;
class PageController extends AbstractController class PageController extends AbstractController
{ {
@ -23,26 +25,48 @@ class PageController extends AbstractController
]); ]);
} }
#[Route('/blog/{page}', name: 'app_blog_home')] #[Route('/blog/{page}', name: 'app_blog_home', defaults: ['page' => 1])]
public function app_blog_home(EntityManagerInterface $entityManagerInterface, $page = 1): Response public function app_blog_home(EntityManagerInterface $entityManagerInterface, Request $request, $page = 1): Response
{ {
$perpage = 9; $perpage = 9;
$posts = $entityManagerInterface->getRepository(Post::class)->findByCat('blog',$perpage,$page); $search = $request->query->get('search', ''); // پارامتر جستجو از URL
$cat = $entityManagerInterface->getRepository(Cat::class)->findOneBy(['code'=>'blog']);
$count = $entityManagerInterface->getRepository(Post::class)->count(['cat'=>$cat]); $postRepository = $entityManagerInterface->getRepository(Post::class);
if(fmod($count,$perpage) == 0){ $catRepository = $entityManagerInterface->getRepository(Cat::class);
$maxpages = $count/$perpage;
// پیدا کردن دسته‌بندی "blog"
$cat = $catRepository->findOneBy(['code' => 'blog']);
// گرفتن پست‌ها با فیلتر جستجو
$queryBuilder = $postRepository->createQueryBuilder('p')
->where('p.cat = :cat')
->setParameter('cat', $cat)
->orderBy('p.dateSubmit', 'DESC'); // مرتب‌سازی بر اساس جدیدترین
if ($search) {
$queryBuilder->andWhere('p.title LIKE :search OR p.intro LIKE :search')
->setParameter('search', "%$search%");
} }
else{
$maxpages = ($count/$perpage) + 1; $count = count($queryBuilder->getQuery()->getResult());
} $maxpages = ceil($count / $perpage); // محاسبه حداکثر صفحات
$maxpages = $count / $perpage;
$posts = $queryBuilder->setMaxResults($perpage)
->setFirstResult(($page - 1) * $perpage)
->getQuery()
->getResult();
// گرفتن همه دسته‌بندی‌ها برای سایدبار
$categories = $catRepository->findAll();
return $this->render('post/blog_home.html.twig', [ return $this->render('post/blog_home.html.twig', [
'posts' => $posts, 'posts' => $posts,
'page' => $page, 'page' => $page,
'perpage' => $perpage, 'perpage' => $perpage,
'count' => $count, 'count' => $count,
'maxpages' => $maxpages 'maxpages' => $maxpages,
'categories' => $categories,
'search' => $search,
]); ]);
} }

View file

@ -14,55 +14,19 @@ class SecurityController extends AbstractController
$error = $authenticationUtils->getLastAuthenticationError(); $error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername(); $lastUsername = $authenticationUtils->getLastUsername();
return $this->render('@EasyAdmin/page/login.html.twig', [ return $this->render('/admin/login.html.twig', [
// parameters usually defined in Symfony login forms
'error' => $error, 'error' => $error,
'last_username' => $lastUsername, 'last_username' => $lastUsername,
// OPTIONAL parameters to customize the login form:
// the translation_domain to use (define this option only if you are
// rendering the login template in a regular Symfony controller; when
// rendering it from an EasyAdmin Dashboard this is automatically set to
// the same domain as the rest of the Dashboard)
'translation_domain' => 'admin', 'translation_domain' => 'admin',
// by default EasyAdmin displays a black square as its default favicon;
// use this method to display a custom favicon: the given path is passed
// "as is" to the Twig asset() function:
//
// the title visible above the login form (define this option only if you are
// rendering the login template in a regular Symfony controller; when rendering
// it from an EasyAdmin Dashboard this is automatically set as the Dashboard title)
'page_title' => 'ورود', 'page_title' => 'ورود',
// the string used to generate the CSRF token. If you don't define
// this parameter, the login form won't include a CSRF token
'csrf_token_intention' => 'authenticate', 'csrf_token_intention' => 'authenticate',
'target_path' => $this->generateUrl('admin', ['_locale' => 'fa']),
// the URL users are redirected to after the login (default: '/admin')
'target_path' => $this->generateUrl('admin'),
// the label displayed for the username form field (the |trans filter is applied to it)
'username_label' => 'پست الکترونیکی', 'username_label' => 'پست الکترونیکی',
// the label displayed for the password form field (the |trans filter is applied to it)
'password_label' => 'کلمه عبور', 'password_label' => 'کلمه عبور',
// the label displayed for the Sign In form button (the |trans filter is applied to it)
'sign_in_label' => 'ورود', 'sign_in_label' => 'ورود',
// whether to enable or not the "forgot password?" link (default: false)
'forgot_password_enabled' => false, 'forgot_password_enabled' => false,
// whether to enable or not the "remember me" checkbox (default: false)
'remember_me_enabled' => true, 'remember_me_enabled' => true,
// whether to check by default the "remember me" checkbox (default: false)
'remember_me_checked' => true, 'remember_me_checked' => true,
// the label displayed for the remember me checkbox (the |trans filter is applied to it)
'remember_me_label' => 'مرا به یاد داشته باش', 'remember_me_label' => 'مرا به یاد داشته باش',
]); ]);
} }

View file

@ -8,9 +8,12 @@ use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\UX\Turbo\Attribute\Broadcast; use Symfony\UX\Turbo\Attribute\Broadcast;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity(repositoryClass: PostRepository::class)] #[ORM\Entity(repositoryClass: PostRepository::class)]
#[Broadcast] #[Broadcast]
#[UniqueEntity(fields: ['url'], message: 'این URL قبلاً استفاده شده است.')]
class Post class Post
{ {
#[ORM\Id] #[ORM\Id]
@ -34,7 +37,8 @@ class Post
#[ORM\Column(nullable: true)] #[ORM\Column(nullable: true)]
private ?bool $publish = null; private ?bool $publish = null;
#[ORM\Column(length: 255, nullable: true)] #[ORM\Column(length: 255, nullable: true, unique: true)]
#[Assert\NotBlank(message: 'URL نمی‌تواند خالی باشد', allowNull: false)]
private ?string $url = null; private ?string $url = null;
#[ORM\Column(length: 255, nullable: true)] #[ORM\Column(length: 255, nullable: true)]
@ -93,7 +97,6 @@ class Post
public function setTitle(?string $title): static public function setTitle(?string $title): static
{ {
$this->title = $title; $this->title = $title;
return $this; return $this;
} }
@ -105,7 +108,6 @@ class Post
public function setSubmitter(?User $submitter): static public function setSubmitter(?User $submitter): static
{ {
$this->submitter = $submitter; $this->submitter = $submitter;
return $this; return $this;
} }
@ -117,7 +119,6 @@ class Post
public function setBody(?string $body): static public function setBody(?string $body): static
{ {
$this->body = $body; $this->body = $body;
return $this; return $this;
} }
@ -129,7 +130,6 @@ class Post
public function setDateSubmit(string $dateSubmit): static public function setDateSubmit(string $dateSubmit): static
{ {
$this->dateSubmit = $dateSubmit; $this->dateSubmit = $dateSubmit;
return $this; return $this;
} }
@ -141,7 +141,6 @@ class Post
public function setPublish(?bool $publish): static public function setPublish(?bool $publish): static
{ {
$this->publish = $publish; $this->publish = $publish;
return $this; return $this;
} }
@ -153,7 +152,6 @@ class Post
public function setUrl(?string $url): static public function setUrl(?string $url): static
{ {
$this->url = $url; $this->url = $url;
return $this; return $this;
} }
@ -165,7 +163,6 @@ class Post
public function setMainPic(?string $mainPic): static public function setMainPic(?string $mainPic): static
{ {
$this->mainPic = $mainPic; $this->mainPic = $mainPic;
return $this; return $this;
} }
@ -177,7 +174,6 @@ class Post
public function setPlain(?string $plain): static public function setPlain(?string $plain): static
{ {
$this->plain = $plain; $this->plain = $plain;
return $this; return $this;
} }
@ -189,7 +185,6 @@ class Post
public function setVersion(?string $version): static public function setVersion(?string $version): static
{ {
$this->version = $version; $this->version = $version;
return $this; return $this;
} }
@ -201,7 +196,6 @@ class Post
public function setKeywords(?string $keywords): static public function setKeywords(?string $keywords): static
{ {
$this->keywords = $keywords; $this->keywords = $keywords;
return $this; return $this;
} }
@ -213,7 +207,6 @@ class Post
public function setSort(?string $sort): static public function setSort(?string $sort): static
{ {
$this->sort = $sort; $this->sort = $sort;
return $this; return $this;
} }
@ -225,7 +218,6 @@ class Post
public function setCat(?Cat $cat): static public function setCat(?Cat $cat): static
{ {
$this->cat = $cat; $this->cat = $cat;
return $this; return $this;
} }
@ -242,14 +234,12 @@ class Post
if (!$this->tree->contains($tree)) { if (!$this->tree->contains($tree)) {
$this->tree->add($tree); $this->tree->add($tree);
} }
return $this; return $this;
} }
public function removeTree(Tree $tree): static public function removeTree(Tree $tree): static
{ {
$this->tree->removeElement($tree); $this->tree->removeElement($tree);
return $this; return $this;
} }
@ -267,19 +257,16 @@ class Post
$this->comments->add($comment); $this->comments->add($comment);
$comment->setPost($this); $comment->setPost($this);
} }
return $this; return $this;
} }
public function removeComment(Comment $comment): static public function removeComment(Comment $comment): static
{ {
if ($this->comments->removeElement($comment)) { if ($this->comments->removeElement($comment)) {
// set the owning side to null (unless already changed)
if ($comment->getPost() === $this) { if ($comment->getPost() === $this) {
$comment->setPost(null); $comment->setPost(null);
} }
} }
return $this; return $this;
} }
@ -291,7 +278,6 @@ class Post
public function setIntro(?string $intro): static public function setIntro(?string $intro): static
{ {
$this->intro = $intro; $this->intro = $intro;
return $this; return $this;
} }
@ -303,7 +289,6 @@ class Post
public function setViews(?string $views): static public function setViews(?string $views): static
{ {
$this->views = $views; $this->views = $views;
return $this; return $this;
} }
} }

View file

@ -0,0 +1,7 @@
{# templates/easy_admin/page/login.html.twig #}
{% extends '@!EasyAdmin/page/login.html.twig' %}
{% block head %}
{{ parent() }}
<link rel="stylesheet" href="/css/login.css">
{% endblock %}