diff --git a/.idea/php.xml b/.idea/php.xml index 66d6fb8..214f408 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -13,6 +13,7 @@ + diff --git a/assets/styles/app.scss b/assets/styles/app.scss index 1f41fc1..fff5ff0 100644 --- a/assets/styles/app.scss +++ b/assets/styles/app.scss @@ -359,20 +359,40 @@ ul.credit { .app-overview-dashboard { display: grid; gap: 1rem; - grid-template: - "hello-text" auto - "announcements" auto - "weekly-metrics" auto - "historic-statistics" auto - "leaderboard" auto; - - @media (min-width: 1440px) { + + &-criterion-reference { + grid-template: + "hello-text" auto + "announcements" auto + "weekly-metrics" auto + "historic-statistics" auto + "leaderboard" auto; + + @media (min-width: 1440px) { + grid-template: + "hello-text hello-text hello-text hello-text" auto + "announcements announcements announcements announcements" auto + "weekly-metrics weekly-metrics leaderboard leaderboard" 1fr + "historic-statistics historic-statistics leaderboard leaderboard" 1fr; + grid-template-columns: 2fr 2fr 1fr 1fr; + } + } + + &-self-reference { grid-template: - "hello-text hello-text hello-text hello-text" auto - "announcements announcements announcements announcements" auto - "weekly-metrics weekly-metrics leaderboard leaderboard" 1fr - "historic-statistics historic-statistics leaderboard leaderboard" 1fr; - grid-template-columns: 2fr 2fr 1fr 1fr; + "hello-text" auto + "announcements" auto + "weekly-metrics" auto + "historic-statistics" auto; + + @media (min-width: 1440px) { + grid-template: + "hello-text hello-text hello-text hello-text" auto + "announcements announcements announcements announcements" auto + "weekly-metrics weekly-metrics weekly-metrics weekly-metrics" 1fr + "historic-statistics historic-statistics historic-statistics historic-statistics" 1fr; + grid-template-columns: 1fr 1fr 1fr 1fr; + } } &__hello-text { diff --git a/migrations/Version20241230103024.php b/migrations/Version20241230103024.php new file mode 100644 index 0000000..58a1238 --- /dev/null +++ b/migrations/Version20241230103024.php @@ -0,0 +1,35 @@ +addSql(<<<'SQL' + ALTER TABLE "group" ADD layout VARCHAR(255) DEFAULT NULL + SQL); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql(<<<'SQL' + ALTER TABLE "group" DROP layout + SQL); + } +} diff --git a/src/Controller/Admin/GroupCrudController.php b/src/Controller/Admin/GroupCrudController.php index f8e94a6..df32999 100644 --- a/src/Controller/Admin/GroupCrudController.php +++ b/src/Controller/Admin/GroupCrudController.php @@ -6,11 +6,14 @@ use App\Entity\Group; use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController; +use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField; use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField; use EasyCorp\Bundle\EasyAdminBundle\Field\IdField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; +use function Symfony\Component\Translation\t; + class GroupCrudController extends AbstractCrudController { public static function getEntityFqcn(): string @@ -20,10 +23,26 @@ public static function getEntityFqcn(): string public function configureFields(string $pageName): iterable { + $availableLayouts = glob(__DIR__.'/../../../templates/overview/layout/*.html.twig'); + if (false === $availableLayouts) { + $availableLayouts = []; + } + + $availableLayouts = array_map(static fn ($path) => pathinfo($path, PATHINFO_BASENAME), $availableLayouts); + if (0 === \count($availableLayouts)) { + $availableLayouts = ['default.html.twig']; + } + + // trim the .html.twig extension + $availableLayouts = array_map(static fn ($layout) => substr($layout, 0, -10), $availableLayouts); + return [ IdField::new('id')->hideOnForm(), TextField::new('name', 'Name'), TextEditorField::new('description', 'Description'), + ChoiceField::new('layout', 'Layout') + ->setChoices(array_combine($availableLayouts, $availableLayouts)) + ->setHelp(t('admin.group.layout.help')), DateTimeField::new('created_at', 'Created at')->hideOnForm(), DateTimeField::new('updated_at', 'Updated at')->hideOnForm(), ]; diff --git a/src/Controller/Admin/UserCrudController.php b/src/Controller/Admin/UserCrudController.php index a6be619..57d07a4 100644 --- a/src/Controller/Admin/UserCrudController.php +++ b/src/Controller/Admin/UserCrudController.php @@ -54,7 +54,7 @@ public function configureFields(string $pageName): iterable AssociationField::new('group', 'Group'), DateTimeField::new('created_at', 'Created at')->hideOnForm(), DateTimeField::new('updated_at', 'Updated at')->hideOnForm(), - DateTimeField::new('last_login_at', 'Last login at'), + DateTimeField::new('last_login_at', 'Last login at')->hideOnForm(), ]; } diff --git a/src/Controller/OverviewController.php b/src/Controller/OverviewController.php index 5f1e2a2..4244fc1 100644 --- a/src/Controller/OverviewController.php +++ b/src/Controller/OverviewController.php @@ -4,17 +4,24 @@ namespace App\Controller; +use App\Entity\User; use App\Repository\AnnouncementRepository; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\CurrentUser; class OverviewController extends AbstractController { #[Route('/overview', name: 'app_overview')] - public function index(): Response + public function index(#[CurrentUser] User $user): Response { - return $this->render('overview/index.html.twig'); + $layoutName = $user->getGroup()?->getLayout() ?? 'default'; + $mainContent = $this->renderView("overview/layout/{$layoutName}.html.twig"); + + return $this->render('overview/index.html.twig', [ + 'mainContent' => $mainContent, + ]); } #[Route('/overview/announcements', name: 'app_overview_announcements')] diff --git a/src/Entity/Group.php b/src/Entity/Group.php index 13b3c72..7ff1d30 100644 --- a/src/Entity/Group.php +++ b/src/Entity/Group.php @@ -34,6 +34,9 @@ class Group #[ORM\OneToMany(targetEntity: User::class, mappedBy: 'group')] private Collection $users; + #[ORM\Column(length: 255, nullable: true)] + private ?string $layout = null; + public function __construct() { $this->users = new ArrayCollection(); @@ -109,4 +112,16 @@ public function removeUser(User $user): static return $this; } + + public function getLayout(): ?string + { + return $this->layout; + } + + public function setLayout(?string $layout): static + { + $this->layout = $layout; + + return $this; + } } diff --git a/templates/overview/index.html.twig b/templates/overview/index.html.twig index c3a336a..755bbf2 100644 --- a/templates/overview/index.html.twig +++ b/templates/overview/index.html.twig @@ -4,53 +4,5 @@ {% block title %}學習概況{% endblock %} {% block app %} -
-
-

哈囉,{{ app.user.name }} 👋

-

這些是你最近的表現 ↓

-
- -
- -
- -
-

每週學習概況

- -
-
-
- - - -
-
-
- -
-
-
- -
-

學習歷程

- -
-
-
- - - -
-
-
- -
-
-
- -
-

週排行榜

- -
-
+{{ mainContent|raw }} {% endblock %} diff --git a/templates/overview/layout/default.html.twig b/templates/overview/layout/default.html.twig new file mode 100644 index 0000000..d4ea8ba --- /dev/null +++ b/templates/overview/layout/default.html.twig @@ -0,0 +1,49 @@ +
+
+

哈囉,{{ app.user.name }} 👋

+

這些是你最近的表現 ↓

+
+ +
+ +
+ +
+

每週學習概況

+ +
+
+
+ + + +
+
+
+ +
+
+
+ +
+

學習歷程

+ +
+
+
+ + + +
+
+
+ +
+
+
+ +
+

週排行榜

+ +
+
diff --git a/templates/overview/layout/self-reference.html.twig b/templates/overview/layout/self-reference.html.twig new file mode 100644 index 0000000..a67da87 --- /dev/null +++ b/templates/overview/layout/self-reference.html.twig @@ -0,0 +1,44 @@ +
+
+

哈囉,{{ app.user.name }} 👋

+

這些是你最近的表現 ↓

+
+ +
+ +
+ +
+

每週學習概況

+ +
+
+
+ + + +
+
+
+ +
+
+
+ +
+

學習歷程

+ +
+
+
+ + + +
+
+
+ +
+
+
+
diff --git a/translations/messages.en_US.yaml b/translations/messages.en_US.yaml index 465b4da..c65e71d 100644 --- a/translations/messages.en_US.yaml +++ b/translations/messages.en_US.yaml @@ -6,3 +6,10 @@ challenge: column-different: The column names differ; please compare and modify according to the correct answer. row-different: The %row% row you answered is different from the correct answer. row-unmatched: The number of returned rows does not match the correct answer (the correct answer has %expected% rows, while you answered %actual% rows). + +admin: + group: + layout: + help: >- + Layout name. Leave blank to use the default layout. + You can create your own layout in the templates/overview/layout directory in the code. diff --git a/translations/messages.zh_TW.yaml b/translations/messages.zh_TW.yaml index 7a026ff..07297a0 100644 --- a/translations/messages.zh_TW.yaml +++ b/translations/messages.zh_TW.yaml @@ -72,6 +72,7 @@ Last login at: 最後登入時間 Statistics: 統計資料 Completed Questions: 完成題數 Experience Points: 經驗值 +Layout: 佈局 result_presenter.tabs.result: 執行結果 result_presenter.tabs.answer: 正確答案 @@ -191,3 +192,10 @@ email-kind: transactional: 通知型信件 marketing: 行銷型信件 test: 測試用信件 + +admin: + group: + layout: + help: >- + 版面名稱。留空代表使用預設版面。 + 可以在程式碼的 templates/overview/layout 目錄製作自己想要的版面。