feat: working on i18n
This commit is contained in:
parent
f0ffe83f83
commit
38774aaaa6
16 changed files with 355 additions and 89 deletions
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use App\Models\Language;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Inertia\Middleware;
|
||||
|
||||
class HandleInertiaRequests extends Middleware
|
||||
|
|
@ -38,6 +39,10 @@ public function share(Request $request): array
|
|||
{
|
||||
$user = $request->user();
|
||||
|
||||
if ($user) {
|
||||
App::setLocale($user->language->code);
|
||||
}
|
||||
|
||||
$currentWorkspace = $user?->currentWorkspace;
|
||||
$currentRole = null;
|
||||
|
||||
|
|
|
|||
42
lang/en/hashtags.php
Normal file
42
lang/en/hashtags.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Hashtags',
|
||||
'description' => 'Create hashtag groups to quickly add to your posts',
|
||||
'new_group' => 'New Group',
|
||||
'no_groups_yet' => 'No hashtag groups yet',
|
||||
'no_groups_description' => 'Create hashtag groups to quickly add popular hashtags to your posts',
|
||||
'create_first_group' => 'Create your first group',
|
||||
'hashtags_count' => ':count hashtags',
|
||||
|
||||
'create' => [
|
||||
'title' => 'Create Hashtag Group',
|
||||
'description' => 'Give your group a name and add hashtags separated by spaces or commas',
|
||||
'name' => 'Group Name',
|
||||
'name_placeholder' => 'e.g. Marketing, Travel, Food',
|
||||
'hashtags' => 'Hashtags',
|
||||
'hashtags_placeholder' => '#marketing #socialmedia #business #growth',
|
||||
'hashtags_hint' => 'Enter hashtags separated by spaces or commas. Include the # symbol.',
|
||||
'submit' => 'Create Group',
|
||||
'submitting' => 'Creating...',
|
||||
],
|
||||
|
||||
'edit' => [
|
||||
'title' => 'Edit Hashtag Group',
|
||||
'description' => 'Update the name and hashtags for this group',
|
||||
'name' => 'Group Name',
|
||||
'name_placeholder' => 'e.g. Marketing, Travel, Food',
|
||||
'hashtags' => 'Hashtags',
|
||||
'hashtags_placeholder' => '#marketing #socialmedia #business #growth',
|
||||
'hashtags_hint' => 'Enter hashtags separated by spaces or commas. Include the # symbol.',
|
||||
'submit' => 'Save Changes',
|
||||
'submitting' => 'Saving...',
|
||||
],
|
||||
|
||||
'delete' => [
|
||||
'title' => 'Delete Hashtag Group',
|
||||
'description' => 'Are you sure you want to delete this hashtag group? This action cannot be undone.',
|
||||
'confirm' => 'Delete',
|
||||
'cancel' => 'Cancel',
|
||||
],
|
||||
];
|
||||
36
lang/en/labels.php
Normal file
36
lang/en/labels.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Labels',
|
||||
'description' => 'Create labels to organize and categorize your posts',
|
||||
'new_label' => 'New Label',
|
||||
'no_labels_yet' => 'No labels yet',
|
||||
'create_first_label' => 'Create your first label',
|
||||
|
||||
'create' => [
|
||||
'title' => 'Create Label',
|
||||
'description' => 'Give your label a name and pick a color',
|
||||
'name' => 'Name',
|
||||
'name_placeholder' => 'Enter label name...',
|
||||
'color' => 'Color',
|
||||
'submit' => 'Create Label',
|
||||
'submitting' => 'Creating...',
|
||||
],
|
||||
|
||||
'edit' => [
|
||||
'title' => 'Edit Label',
|
||||
'description' => 'Update the name and color for this label',
|
||||
'name' => 'Name',
|
||||
'name_placeholder' => 'Enter label name...',
|
||||
'color' => 'Color',
|
||||
'submit' => 'Save Changes',
|
||||
'submitting' => 'Saving...',
|
||||
],
|
||||
|
||||
'delete' => [
|
||||
'title' => 'Delete Label',
|
||||
'description' => 'Are you sure you want to delete this label? This action cannot be undone.',
|
||||
'confirm' => 'Delete',
|
||||
'cancel' => 'Cancel',
|
||||
],
|
||||
];
|
||||
48
lang/en/sidebar.php
Normal file
48
lang/en/sidebar.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'select_workspace' => 'Select workspace',
|
||||
'create_workspace' => 'Create workspace',
|
||||
'create_post' => 'Create post',
|
||||
'profile' => 'Profile',
|
||||
'log_out' => 'Log out',
|
||||
|
||||
'workspace' => 'Workspace: :name',
|
||||
'workspace_select' => 'Workspace: Select',
|
||||
|
||||
'theme' => 'Theme: :name',
|
||||
'theme_light' => 'Light',
|
||||
'theme_dark' => 'Dark',
|
||||
'theme_system' => 'System',
|
||||
|
||||
'language' => 'Language: :name',
|
||||
'language_select' => 'Language: Select',
|
||||
|
||||
'groups' => [
|
||||
'posts' => 'Posts',
|
||||
'configuration' => 'Configurations',
|
||||
'support' => 'Support',
|
||||
],
|
||||
|
||||
'posts' => [
|
||||
'calendar' => 'Calendar',
|
||||
'all' => 'All',
|
||||
'scheduled' => 'Scheduled',
|
||||
'posted' => 'Posted',
|
||||
'drafts' => 'Drafts',
|
||||
],
|
||||
|
||||
'config' => [
|
||||
'connections' => 'Connections',
|
||||
'hashtags' => 'Hashtags',
|
||||
'labels' => 'Labels',
|
||||
'settings' => 'Settings',
|
||||
],
|
||||
|
||||
'support' => [
|
||||
'discord' => 'Discord',
|
||||
'share_feedback' => 'Share feedback',
|
||||
'last_updates' => 'Last Updates',
|
||||
'docs' => 'Docs',
|
||||
],
|
||||
];
|
||||
1
lang/php_en.json
Normal file
1
lang/php_en.json
Normal file
File diff suppressed because one or more lines are too long
1
lang/php_pt-BR.json
Normal file
1
lang/php_pt-BR.json
Normal file
File diff suppressed because one or more lines are too long
42
lang/pt-BR/hashtags.php
Normal file
42
lang/pt-BR/hashtags.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Hashtags',
|
||||
'description' => 'Crie grupos de hashtags para adicionar rapidamente aos seus posts',
|
||||
'new_group' => 'Novo Grupo',
|
||||
'no_groups_yet' => 'Nenhum grupo de hashtags ainda',
|
||||
'no_groups_description' => 'Crie grupos de hashtags para adicionar rapidamente hashtags populares aos seus posts',
|
||||
'create_first_group' => 'Crie seu primeiro grupo',
|
||||
'hashtags_count' => ':count hashtags',
|
||||
|
||||
'create' => [
|
||||
'title' => 'Criar Grupo de Hashtags',
|
||||
'description' => 'Dê um nome ao grupo e adicione hashtags separadas por espaços ou vírgulas',
|
||||
'name' => 'Nome do Grupo',
|
||||
'name_placeholder' => 'ex: Marketing, Viagem, Comida',
|
||||
'hashtags' => 'Hashtags',
|
||||
'hashtags_placeholder' => '#marketing #redessociais #negocios #crescimento',
|
||||
'hashtags_hint' => 'Digite as hashtags separadas por espaços ou vírgulas. Inclua o símbolo #.',
|
||||
'submit' => 'Criar Grupo',
|
||||
'submitting' => 'Criando...',
|
||||
],
|
||||
|
||||
'edit' => [
|
||||
'title' => 'Editar Grupo de Hashtags',
|
||||
'description' => 'Atualize o nome e as hashtags deste grupo',
|
||||
'name' => 'Nome do Grupo',
|
||||
'name_placeholder' => 'ex: Marketing, Viagem, Comida',
|
||||
'hashtags' => 'Hashtags',
|
||||
'hashtags_placeholder' => '#marketing #redessociais #negocios #crescimento',
|
||||
'hashtags_hint' => 'Digite as hashtags separadas por espaços ou vírgulas. Inclua o símbolo #.',
|
||||
'submit' => 'Salvar Alterações',
|
||||
'submitting' => 'Salvando...',
|
||||
],
|
||||
|
||||
'delete' => [
|
||||
'title' => 'Excluir Grupo de Hashtags',
|
||||
'description' => 'Tem certeza que deseja excluir este grupo de hashtags? Esta ação não pode ser desfeita.',
|
||||
'confirm' => 'Excluir',
|
||||
'cancel' => 'Cancelar',
|
||||
],
|
||||
];
|
||||
36
lang/pt-BR/labels.php
Normal file
36
lang/pt-BR/labels.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Etiquetas',
|
||||
'description' => 'Crie etiquetas para organizar e categorizar seus posts',
|
||||
'new_label' => 'Nova Etiqueta',
|
||||
'no_labels_yet' => 'Nenhuma etiqueta ainda',
|
||||
'create_first_label' => 'Crie sua primeira etiqueta',
|
||||
|
||||
'create' => [
|
||||
'title' => 'Criar Etiqueta',
|
||||
'description' => 'Dê um nome e escolha uma cor para sua etiqueta',
|
||||
'name' => 'Nome',
|
||||
'name_placeholder' => 'Digite o nome da etiqueta...',
|
||||
'color' => 'Cor',
|
||||
'submit' => 'Criar Etiqueta',
|
||||
'submitting' => 'Criando...',
|
||||
],
|
||||
|
||||
'edit' => [
|
||||
'title' => 'Editar Etiqueta',
|
||||
'description' => 'Atualize o nome e a cor desta etiqueta',
|
||||
'name' => 'Nome',
|
||||
'name_placeholder' => 'Digite o nome da etiqueta...',
|
||||
'color' => 'Cor',
|
||||
'submit' => 'Salvar Alterações',
|
||||
'submitting' => 'Salvando...',
|
||||
],
|
||||
|
||||
'delete' => [
|
||||
'title' => 'Excluir Etiqueta',
|
||||
'description' => 'Tem certeza que deseja excluir esta etiqueta? Esta ação não pode ser desfeita.',
|
||||
'confirm' => 'Excluir',
|
||||
'cancel' => 'Cancelar',
|
||||
],
|
||||
];
|
||||
48
lang/pt-BR/sidebar.php
Normal file
48
lang/pt-BR/sidebar.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'select_workspace' => 'Selecionar workspace',
|
||||
'create_workspace' => 'Criar workspace',
|
||||
'create_post' => 'Criar post',
|
||||
'profile' => 'Perfil',
|
||||
'log_out' => 'Sair',
|
||||
|
||||
'workspace' => 'Workspace: :name',
|
||||
'workspace_select' => 'Workspace: Selecionar',
|
||||
|
||||
'theme' => 'Tema: :name',
|
||||
'theme_light' => 'Claro',
|
||||
'theme_dark' => 'Escuro',
|
||||
'theme_system' => 'Sistema',
|
||||
|
||||
'language' => 'Idioma: :name',
|
||||
'language_select' => 'Idioma: Selecionar',
|
||||
|
||||
'groups' => [
|
||||
'posts' => 'Posts',
|
||||
'configuration' => 'Configurações',
|
||||
'support' => 'Suporte',
|
||||
],
|
||||
|
||||
'posts' => [
|
||||
'calendar' => 'Calendário',
|
||||
'all' => 'Todos',
|
||||
'scheduled' => 'Agendados',
|
||||
'posted' => 'Publicados',
|
||||
'drafts' => 'Rascunhos',
|
||||
],
|
||||
|
||||
'config' => [
|
||||
'connections' => 'Conexões',
|
||||
'hashtags' => 'Hashtags',
|
||||
'labels' => 'Etiquetas',
|
||||
'settings' => 'Configurações',
|
||||
],
|
||||
|
||||
'support' => [
|
||||
'discord' => 'Discord',
|
||||
'share_feedback' => 'Enviar feedback',
|
||||
'last_updates' => 'Últimas Atualizações',
|
||||
'docs' => 'Documentação',
|
||||
],
|
||||
];
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { Link, router, usePage } from '@inertiajs/vue3';
|
||||
import { IconBrandDiscord, IconCalendar, IconCheck, IconSelector, IconFileText, IconHash, IconLogout, IconPlus, IconSettings, IconAffiliate, IconPencil, IconFileCheck, IconTag, IconUser, IconClock, IconMessageCircle, IconBell, IconBook, IconSun, IconMoon, IconDeviceDesktop, IconLanguage } from '@tabler/icons-vue';
|
||||
import { trans } from 'laravel-vue-i18n';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { create as createPost } from '@/actions/App/Http/Controllers/PostController';
|
||||
import { updateLanguage } from '@/actions/App/Http/Controllers/Settings/ProfileController';
|
||||
|
|
@ -72,56 +73,56 @@ const { getInitials } = useInitials();
|
|||
const { appearance, updateAppearance } = useAppearance();
|
||||
const { state: sidebarState } = useSidebar();
|
||||
|
||||
const themeLabels: Record<string, string> = {
|
||||
light: 'Light',
|
||||
dark: 'Dark',
|
||||
system: 'System',
|
||||
};
|
||||
const themeLabels = computed(() => ({
|
||||
light: trans('sidebar.theme_light'),
|
||||
dark: trans('sidebar.theme_dark'),
|
||||
system: trans('sidebar.theme_system'),
|
||||
}));
|
||||
|
||||
const postsNavItems: NavItem[] = [
|
||||
const postsNavItems = computed<NavItem[]>(() => [
|
||||
{
|
||||
title: 'Calendar',
|
||||
title: trans('sidebar.posts.calendar'),
|
||||
href: calendar.url(),
|
||||
icon: IconCalendar,
|
||||
},
|
||||
{
|
||||
title: 'All',
|
||||
title: trans('sidebar.posts.all'),
|
||||
href: postsIndex.url(),
|
||||
icon: IconFileText,
|
||||
},
|
||||
{
|
||||
title: 'Scheduled',
|
||||
title: trans('sidebar.posts.scheduled'),
|
||||
href: postsIndex.url('scheduled'),
|
||||
icon: IconClock,
|
||||
},
|
||||
{
|
||||
title: 'Posted',
|
||||
title: trans('sidebar.posts.posted'),
|
||||
href: postsIndex.url('published'),
|
||||
icon: IconFileCheck,
|
||||
},
|
||||
{
|
||||
title: 'Drafts',
|
||||
title: trans('sidebar.posts.drafts'),
|
||||
href: postsIndex.url('draft'),
|
||||
icon: IconPencil,
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
const canManageWorkspace = computed(() => auth.value.role !== 'member');
|
||||
|
||||
const configNavItems = computed(() => {
|
||||
const items: NavItem[] = [
|
||||
{
|
||||
title: 'Connections',
|
||||
title: trans('sidebar.config.connections'),
|
||||
href: accounts.url(),
|
||||
icon: IconAffiliate,
|
||||
},
|
||||
{
|
||||
title: 'Hashtags',
|
||||
title: trans('sidebar.config.hashtags'),
|
||||
href: hashtags.url(),
|
||||
icon: IconHash,
|
||||
},
|
||||
{
|
||||
title: 'Labels',
|
||||
title: trans('sidebar.config.labels'),
|
||||
href: labels.url(),
|
||||
icon: IconTag,
|
||||
},
|
||||
|
|
@ -129,7 +130,7 @@ const configNavItems = computed(() => {
|
|||
|
||||
if (canManageWorkspace.value) {
|
||||
items.push({
|
||||
title: 'Settings',
|
||||
title: trans('sidebar.config.settings'),
|
||||
href: workspaceSettings.url(),
|
||||
icon: IconSettings,
|
||||
});
|
||||
|
|
@ -138,28 +139,28 @@ const configNavItems = computed(() => {
|
|||
return items;
|
||||
});
|
||||
|
||||
const supportNavItems: NavItem[] = [
|
||||
const supportNavItems = computed<NavItem[]>(() => [
|
||||
{
|
||||
title: 'Discord',
|
||||
title: trans('sidebar.support.discord'),
|
||||
href: 'https://trypost.it/discord',
|
||||
icon: IconBrandDiscord,
|
||||
},
|
||||
{
|
||||
title: 'Share feedback',
|
||||
title: trans('sidebar.support.share_feedback'),
|
||||
href: 'https://github.com/trypost-it/trypost/discussions',
|
||||
icon: IconMessageCircle,
|
||||
},
|
||||
{
|
||||
title: 'Last Updates',
|
||||
title: trans('sidebar.support.last_updates'),
|
||||
href: 'https://github.com/trypost-it/trypost/releases',
|
||||
icon: IconBell,
|
||||
},
|
||||
{
|
||||
title: 'Docs',
|
||||
title: trans('sidebar.support.docs'),
|
||||
href: 'https://docs.trypost.it',
|
||||
icon: IconBook,
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
function switchWorkspace(workspace: Workspace) {
|
||||
router.post(switchMethod.url(workspace.id), {}, {
|
||||
|
|
@ -206,7 +207,7 @@ function handleLogout() {
|
|||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-semibold">{{ auth.user.name }}</span>
|
||||
<span class="truncate text-xs text-muted-foreground">{{ currentWorkspace?.name ||
|
||||
'Select workspace' }}</span>
|
||||
$t('sidebar.select_workspace') }}</span>
|
||||
</div>
|
||||
<IconSelector class="ml-auto size-4" />
|
||||
</SidebarMenuButton>
|
||||
|
|
@ -237,7 +238,7 @@ function handleLogout() {
|
|||
<DropdownMenuSubTrigger>
|
||||
<img v-if="currentWorkspace?.logo.url" :src="currentWorkspace.logo.url"
|
||||
:alt="currentWorkspace.name" class="mr-2 size-4 rounded-full object-cover" />
|
||||
<span>Workspace: {{ currentWorkspace?.name || 'Select' }}</span>
|
||||
<span>{{ currentWorkspace ? $t('sidebar.workspace', { name: currentWorkspace.name }) : $t('sidebar.workspace_select') }}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuSubContent>
|
||||
|
|
@ -253,7 +254,7 @@ function handleLogout() {
|
|||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem class="cursor-pointer gap-2" @click="createWorkspace">
|
||||
<IconPlus class="size-4" />
|
||||
<span>Create workspace</span>
|
||||
<span>{{ $t('sidebar.create_workspace') }}</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuPortal>
|
||||
|
|
@ -267,7 +268,7 @@ function handleLogout() {
|
|||
<IconSun v-if="appearance === 'light'" class="mr-2 size-4" />
|
||||
<IconMoon v-else-if="appearance === 'dark'" class="mr-2 size-4" />
|
||||
<IconDeviceDesktop v-else class="mr-2 size-4" />
|
||||
<span>Theme: {{ themeLabels[appearance] }}</span>
|
||||
<span>{{ $t('sidebar.theme', { name: themeLabels[appearance] }) }}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuSubContent>
|
||||
|
|
@ -275,15 +276,15 @@ function handleLogout() {
|
|||
@update:model-value="updateAppearance">
|
||||
<DropdownMenuRadioItem value="light">
|
||||
<IconSun class="mr-2 size-4" />
|
||||
<span>Light</span>
|
||||
<span>{{ $t('sidebar.theme_light') }}</span>
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="dark">
|
||||
<IconMoon class="mr-2 size-4" />
|
||||
<span>Dark</span>
|
||||
<span>{{ $t('sidebar.theme_dark') }}</span>
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="system">
|
||||
<IconDeviceDesktop class="mr-2 size-4" />
|
||||
<span>System</span>
|
||||
<span>{{ $t('sidebar.theme_system') }}</span>
|
||||
</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuSubContent>
|
||||
|
|
@ -296,7 +297,7 @@ function handleLogout() {
|
|||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<IconLanguage class="mr-2 size-4" />
|
||||
<span>Language: {{ currentLanguage?.name || 'Select' }}</span>
|
||||
<span>{{ currentLanguage ? $t('sidebar.language', { name: currentLanguage.name }) : $t('sidebar.language_select') }}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuSubContent>
|
||||
|
|
@ -318,7 +319,7 @@ function handleLogout() {
|
|||
<DropdownMenuItem as-child>
|
||||
<Link class="cursor-pointer" :href="editProfile()">
|
||||
<IconUser class="mr-2 size-4" />
|
||||
Profile
|
||||
{{ $t('sidebar.profile') }}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
|
|
@ -329,7 +330,7 @@ function handleLogout() {
|
|||
<DropdownMenuItem as-child>
|
||||
<Link class="w-full cursor-pointer" :href="logout()" @click="handleLogout" as="button">
|
||||
<IconLogout class="mr-2 size-4" />
|
||||
Log out
|
||||
{{ $t('sidebar.log_out') }}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
|
|
@ -344,13 +345,13 @@ function handleLogout() {
|
|||
<Link :href="createPost.url()">
|
||||
<Button :size="sidebarState === 'collapsed' ? 'icon' : 'default'" class="w-full">
|
||||
<IconPlus class="size-4" />
|
||||
<span v-if="sidebarState === 'expanded'">Create post</span>
|
||||
<span v-if="sidebarState === 'expanded'">{{ $t('sidebar.create_post') }}</span>
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<SidebarGroup v-if="currentWorkspace">
|
||||
<SidebarGroupLabel>Posts</SidebarGroupLabel>
|
||||
<SidebarGroupLabel>{{ $t('sidebar.groups.posts') }}</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem v-for="item in postsNavItems" :key="item.title">
|
||||
|
|
@ -367,7 +368,7 @@ function handleLogout() {
|
|||
</SidebarGroup>
|
||||
|
||||
<SidebarGroup v-if="currentWorkspace">
|
||||
<SidebarGroupLabel>Configuration</SidebarGroupLabel>
|
||||
<SidebarGroupLabel>{{ $t('sidebar.groups.configuration') }}</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem v-for="item in configNavItems" :key="item.title">
|
||||
|
|
@ -384,7 +385,7 @@ function handleLogout() {
|
|||
</SidebarGroup>
|
||||
|
||||
<SidebarGroup v-if="currentWorkspace">
|
||||
<SidebarGroupLabel>Support</SidebarGroupLabel>
|
||||
<SidebarGroupLabel>{{ $t('sidebar.groups.support') }}</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem v-for="item in supportNavItems" :key="item.title">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useForm } from '@inertiajs/vue3';
|
||||
import { trans } from 'laravel-vue-i18n';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
|
|
@ -44,18 +45,18 @@ const handleOpenChange = (value: boolean) => {
|
|||
<Dialog :open="open" @update:open="handleOpenChange">
|
||||
<DialogContent class="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create Hashtag Group</DialogTitle>
|
||||
<DialogTitle>{{ $t('hashtags.create.title') }}</DialogTitle>
|
||||
<DialogDescription>
|
||||
Give your group a name and add hashtags separated by spaces or commas
|
||||
{{ $t('hashtags.create.description') }}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<form @submit.prevent="submit" class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="create-name">Group Name</Label>
|
||||
<Label for="create-name">{{ $t('hashtags.create.name') }}</Label>
|
||||
<Input
|
||||
id="create-name"
|
||||
v-model="form.name"
|
||||
placeholder="e.g. Marketing, Travel, Food"
|
||||
:placeholder="trans('hashtags.create.name_placeholder')"
|
||||
:class="{ 'border-destructive': form.errors.name }"
|
||||
/>
|
||||
<p v-if="form.errors.name" class="text-sm text-destructive">
|
||||
|
|
@ -64,16 +65,16 @@ const handleOpenChange = (value: boolean) => {
|
|||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="create-hashtags">Hashtags</Label>
|
||||
<Label for="create-hashtags">{{ $t('hashtags.create.hashtags') }}</Label>
|
||||
<Textarea
|
||||
id="create-hashtags"
|
||||
v-model="form.hashtags"
|
||||
placeholder="#marketing #socialmedia #business #growth"
|
||||
:placeholder="trans('hashtags.create.hashtags_placeholder')"
|
||||
rows="4"
|
||||
:class="{ 'border-destructive': form.errors.hashtags }"
|
||||
/>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Enter hashtags separated by spaces or commas. Include the # symbol.
|
||||
{{ $t('hashtags.create.hashtags_hint') }}
|
||||
</p>
|
||||
<p v-if="form.errors.hashtags" class="text-sm text-destructive">
|
||||
{{ form.errors.hashtags }}
|
||||
|
|
@ -82,7 +83,7 @@ const handleOpenChange = (value: boolean) => {
|
|||
|
||||
<DialogFooter>
|
||||
<Button type="submit" :disabled="form.processing">
|
||||
{{ form.processing ? 'Creating...' : 'Create Group' }}
|
||||
{{ form.processing ? $t('hashtags.create.submitting') : $t('hashtags.create.submit') }}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useForm } from '@inertiajs/vue3';
|
||||
import { trans } from 'laravel-vue-i18n';
|
||||
import { watch } from 'vue';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
|
@ -55,18 +56,18 @@ const submit = () => {
|
|||
<Dialog v-model:open="open">
|
||||
<DialogContent class="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Edit Hashtag Group</DialogTitle>
|
||||
<DialogTitle>{{ $t('hashtags.edit.title') }}</DialogTitle>
|
||||
<DialogDescription>
|
||||
Update the name and hashtags for this group
|
||||
{{ $t('hashtags.edit.description') }}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<form @submit.prevent="submit" class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-name">Group Name</Label>
|
||||
<Label for="edit-name">{{ $t('hashtags.edit.name') }}</Label>
|
||||
<Input
|
||||
id="edit-name"
|
||||
v-model="form.name"
|
||||
placeholder="e.g. Marketing, Travel, Food"
|
||||
:placeholder="trans('hashtags.edit.name_placeholder')"
|
||||
:class="{ 'border-destructive': form.errors.name }"
|
||||
/>
|
||||
<p v-if="form.errors.name" class="text-sm text-destructive">
|
||||
|
|
@ -75,16 +76,16 @@ const submit = () => {
|
|||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-hashtags">Hashtags</Label>
|
||||
<Label for="edit-hashtags">{{ $t('hashtags.edit.hashtags') }}</Label>
|
||||
<Textarea
|
||||
id="edit-hashtags"
|
||||
v-model="form.hashtags"
|
||||
placeholder="#marketing #socialmedia #business #growth"
|
||||
:placeholder="trans('hashtags.edit.hashtags_placeholder')"
|
||||
rows="4"
|
||||
:class="{ 'border-destructive': form.errors.hashtags }"
|
||||
/>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Enter hashtags separated by spaces or commas. Include the # symbol.
|
||||
{{ $t('hashtags.edit.hashtags_hint') }}
|
||||
</p>
|
||||
<p v-if="form.errors.hashtags" class="text-sm text-destructive">
|
||||
{{ form.errors.hashtags }}
|
||||
|
|
@ -93,7 +94,7 @@ const submit = () => {
|
|||
|
||||
<DialogFooter>
|
||||
<Button type="submit" :disabled="form.processing">
|
||||
{{ form.processing ? 'Saving...' : 'Save Changes' }}
|
||||
{{ form.processing ? $t('hashtags.edit.submitting') : $t('hashtags.edit.submit') }}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useForm } from '@inertiajs/vue3';
|
||||
import { trans } from 'laravel-vue-i18n';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
|
|
@ -56,18 +57,18 @@ const handleOpenChange = (value: boolean) => {
|
|||
<Dialog :open="open" @update:open="handleOpenChange">
|
||||
<DialogContent class="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create Label</DialogTitle>
|
||||
<DialogTitle>{{ $t('labels.create.title') }}</DialogTitle>
|
||||
<DialogDescription>
|
||||
Give your label a name and pick a color
|
||||
{{ $t('labels.create.description') }}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<form @submit.prevent="submit" class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="create-name">Name</Label>
|
||||
<Label for="create-name">{{ $t('labels.create.name') }}</Label>
|
||||
<Input
|
||||
id="create-name"
|
||||
v-model="form.name"
|
||||
placeholder="Enter label name..."
|
||||
:placeholder="trans('labels.create.name_placeholder')"
|
||||
:class="{ 'border-destructive': form.errors.name }"
|
||||
/>
|
||||
<p v-if="form.errors.name" class="text-sm text-destructive">
|
||||
|
|
@ -76,7 +77,7 @@ const handleOpenChange = (value: boolean) => {
|
|||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label>Color</Label>
|
||||
<Label>{{ $t('labels.create.color') }}</Label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button
|
||||
v-for="color in colors"
|
||||
|
|
@ -99,7 +100,7 @@ const handleOpenChange = (value: boolean) => {
|
|||
|
||||
<DialogFooter>
|
||||
<Button type="submit" :disabled="form.processing">
|
||||
{{ form.processing ? 'Creating...' : 'Create Label' }}
|
||||
{{ form.processing ? $t('labels.create.submitting') : $t('labels.create.submit') }}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useForm } from '@inertiajs/vue3';
|
||||
import { trans } from 'laravel-vue-i18n';
|
||||
import { watch } from 'vue';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
|
@ -66,18 +67,18 @@ const submit = () => {
|
|||
<Dialog v-model:open="open">
|
||||
<DialogContent class="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Edit Label</DialogTitle>
|
||||
<DialogTitle>{{ $t('labels.edit.title') }}</DialogTitle>
|
||||
<DialogDescription>
|
||||
Update the name and color for this label
|
||||
{{ $t('labels.edit.description') }}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<form @submit.prevent="submit" class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="edit-name">Name</Label>
|
||||
<Label for="edit-name">{{ $t('labels.edit.name') }}</Label>
|
||||
<Input
|
||||
id="edit-name"
|
||||
v-model="form.name"
|
||||
placeholder="Enter label name..."
|
||||
:placeholder="trans('labels.edit.name_placeholder')"
|
||||
:class="{ 'border-destructive': form.errors.name }"
|
||||
/>
|
||||
<p v-if="form.errors.name" class="text-sm text-destructive">
|
||||
|
|
@ -86,7 +87,7 @@ const submit = () => {
|
|||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label>Color</Label>
|
||||
<Label>{{ $t('labels.edit.color') }}</Label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button
|
||||
v-for="color in colors"
|
||||
|
|
@ -109,7 +110,7 @@ const submit = () => {
|
|||
|
||||
<DialogFooter>
|
||||
<Button type="submit" :disabled="form.processing">
|
||||
{{ form.processing ? 'Saving...' : 'Save Changes' }}
|
||||
{{ form.processing ? $t('labels.edit.submitting') : $t('labels.edit.submit') }}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { Head } from '@inertiajs/vue3';
|
||||
import { IconPlus, IconHash, IconPencil, IconTrash } from '@tabler/icons-vue';
|
||||
import { trans } from 'laravel-vue-i18n';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import ConfirmDeleteModal from '@/components/ConfirmDeleteModal.vue';
|
||||
|
|
@ -37,7 +38,7 @@ const isEditDialogOpen = ref(false);
|
|||
const editingHashtag = ref<Hashtag | null>(null);
|
||||
|
||||
const breadcrumbs: BreadcrumbItemType[] = [
|
||||
{ title: 'Hashtags', href: hashtagsIndex.url() },
|
||||
{ title: trans('hashtags.title'), href: hashtagsIndex.url() },
|
||||
];
|
||||
|
||||
const openEditDialog = (hashtag: Hashtag) => {
|
||||
|
|
@ -57,20 +58,20 @@ const getHashtagCount = (hashtags: string): number => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Head title="Hashtags" />
|
||||
<Head :title="$t('hashtags.title')" />
|
||||
|
||||
<AppLayout :breadcrumbs="breadcrumbs">
|
||||
<div class="flex flex-col gap-6 p-6">
|
||||
<div v-if="hashtags.length > 0" class="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold tracking-tight">Hashtags</h1>
|
||||
<h1 class="text-2xl font-bold tracking-tight">{{ $t('hashtags.title') }}</h1>
|
||||
<p class="text-muted-foreground">
|
||||
Create hashtag groups to quickly add to your posts
|
||||
{{ $t('hashtags.description') }}
|
||||
</p>
|
||||
</div>
|
||||
<Button @click="isCreateDialogOpen = true">
|
||||
<IconPlus class="mr-2 h-4 w-4" />
|
||||
New Group
|
||||
{{ $t('hashtags.new_group') }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
|
@ -78,13 +79,13 @@ const getHashtagCount = (hashtags: string): number => {
|
|||
<div class="h-16 w-16 rounded-full bg-muted flex items-center justify-center mb-4">
|
||||
<IconHash class="h-8 w-8 text-muted-foreground" />
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold mb-2">No hashtag groups yet</h3>
|
||||
<h3 class="text-lg font-semibold mb-2">{{ $t('hashtags.no_groups_yet') }}</h3>
|
||||
<p class="text-muted-foreground mb-4 text-center max-w-sm">
|
||||
Create hashtag groups to quickly add popular hashtags to your posts
|
||||
{{ $t('hashtags.no_groups_description') }}
|
||||
</p>
|
||||
<Button @click="isCreateDialogOpen = true">
|
||||
<IconPlus class="mr-2 h-4 w-4" />
|
||||
Create your first group
|
||||
{{ $t('hashtags.create_first_group') }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
|
@ -113,7 +114,7 @@ const getHashtagCount = (hashtags: string): number => {
|
|||
</div>
|
||||
</div>
|
||||
<CardDescription>
|
||||
{{ getHashtagCount(hashtag.hashtags) }} hashtags
|
||||
{{ $t('hashtags.hashtags_count', { count: getHashtagCount(hashtag.hashtags) }) }}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
|
|
@ -131,9 +132,9 @@ const getHashtagCount = (hashtags: string): number => {
|
|||
|
||||
<ConfirmDeleteModal
|
||||
ref="deleteModal"
|
||||
title="Delete Hashtag Group"
|
||||
description="Are you sure you want to delete this hashtag group? This action cannot be undone."
|
||||
action="Delete"
|
||||
cancel="Cancel"
|
||||
:title="$t('hashtags.delete.title')"
|
||||
:description="$t('hashtags.delete.description')"
|
||||
:action="$t('hashtags.delete.confirm')"
|
||||
:cancel="$t('hashtags.delete.cancel')"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { Head } from '@inertiajs/vue3';
|
||||
import { IconPlus, IconTag, IconPencil, IconTrash } from '@tabler/icons-vue';
|
||||
import { trans } from 'laravel-vue-i18n';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import ConfirmDeleteModal from '@/components/ConfirmDeleteModal.vue';
|
||||
|
|
@ -37,7 +38,7 @@ const isEditDialogOpen = ref(false);
|
|||
const editingLabel = ref<Label | null>(null);
|
||||
|
||||
const breadcrumbs: BreadcrumbItemType[] = [
|
||||
{ title: 'Labels', href: labelsIndex.url() },
|
||||
{ title: trans('labels.title'), href: labelsIndex.url() },
|
||||
];
|
||||
|
||||
const openEditDialog = (label: Label) => {
|
||||
|
|
@ -53,20 +54,20 @@ const handleDelete = (labelId: string) => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Head title="Labels" />
|
||||
<Head :title="$t('labels.title')" />
|
||||
|
||||
<AppLayout :breadcrumbs="breadcrumbs">
|
||||
<div class="flex flex-col gap-6 p-6">
|
||||
<div v-if="labels.length > 0" class="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold tracking-tight">Labels</h1>
|
||||
<h1 class="text-2xl font-bold tracking-tight">{{ $t('labels.title') }}</h1>
|
||||
<p class="text-muted-foreground">
|
||||
Create labels to organize and categorize your posts
|
||||
{{ $t('labels.description') }}
|
||||
</p>
|
||||
</div>
|
||||
<Button @click="isCreateDialogOpen = true">
|
||||
<IconPlus class="mr-2 h-4 w-4" />
|
||||
New Label
|
||||
{{ $t('labels.new_label') }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
|
@ -74,13 +75,13 @@ const handleDelete = (labelId: string) => {
|
|||
<div class="h-16 w-16 rounded-full bg-muted flex items-center justify-center mb-4">
|
||||
<IconTag class="h-8 w-8 text-muted-foreground" />
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold mb-2">No labels yet</h3>
|
||||
<h3 class="text-lg font-semibold mb-2">{{ $t('labels.no_labels_yet') }}</h3>
|
||||
<p class="text-muted-foreground mb-4 text-center max-w-sm">
|
||||
Create labels to organize and categorize your posts
|
||||
{{ $t('labels.description') }}
|
||||
</p>
|
||||
<Button @click="isCreateDialogOpen = true">
|
||||
<IconPlus class="mr-2 h-4 w-4" />
|
||||
Create your first label
|
||||
{{ $t('labels.create_first_label') }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
|
@ -125,9 +126,9 @@ const handleDelete = (labelId: string) => {
|
|||
|
||||
<ConfirmDeleteModal
|
||||
ref="deleteModal"
|
||||
title="Delete Label"
|
||||
description="Are you sure you want to delete this label? This action cannot be undone."
|
||||
action="Delete"
|
||||
cancel="Cancel"
|
||||
:title="$t('labels.delete.title')"
|
||||
:description="$t('labels.delete.description')"
|
||||
:action="$t('labels.delete.confirm')"
|
||||
:cancel="$t('labels.delete.cancel')"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
Loading…
Reference in a new issue