mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-17 04:32:15 +01:00
hide actions and pages in the frontend according to permissions
This commit is contained in:
@@ -27,7 +27,6 @@ class ShareInertiaData
|
|||||||
/** @var PermissionStore $permissions */
|
/** @var PermissionStore $permissions */
|
||||||
$permissions = app(PermissionStore::class);
|
$permissions = app(PermissionStore::class);
|
||||||
Inertia::share(array_filter([
|
Inertia::share(array_filter([
|
||||||
'permissions' => $request->user() !== null && $request->user()->currentTeam !== null ? $permissions->getPermissions($request->user()->currentTeam) : [],
|
|
||||||
'jetstream' => function () use ($request) {
|
'jetstream' => function () use ($request) {
|
||||||
/** @var User|null $user */
|
/** @var User|null $user */
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
@@ -49,6 +48,7 @@ class ShareInertiaData
|
|||||||
];
|
];
|
||||||
},
|
},
|
||||||
'auth' => [
|
'auth' => [
|
||||||
|
'permissions' => $request->user() !== null && $request->user()->currentTeam !== null ? $permissions->getPermissions($request->user()->currentTeam) : [],
|
||||||
'user' => function () use ($request): array {
|
'user' => function () use ($request): array {
|
||||||
/** @var User|null $user */
|
/** @var User|null $user */
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import Dropdown from '@/Components/Dropdown.vue';
|
import Dropdown from '@/Components/Dropdown.vue';
|
||||||
import { TrashIcon } from '@heroicons/vue/20/solid';
|
import { TrashIcon } from '@heroicons/vue/20/solid';
|
||||||
import type { Client } from '@/utils/api';
|
import type { Client } from '@/utils/api';
|
||||||
|
import { canDeleteClients } from '@/utils/permissions';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
delete: [];
|
delete: [];
|
||||||
@@ -31,6 +32,7 @@ const props = defineProps<{
|
|||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<button
|
<button
|
||||||
|
v-if="canDeleteClients()"
|
||||||
@click="emit('delete')"
|
@click="emit('delete')"
|
||||||
:aria-label="'Delete Client ' + props.client.name"
|
:aria-label="'Delete Client ' + props.client.name"
|
||||||
data-testid="client_delete"
|
data-testid="client_delete"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useClientsStore } from '@/utils/useClients';
|
|||||||
import ClientTableRow from '@/Components/Common/Client/ClientTableRow.vue';
|
import ClientTableRow from '@/Components/Common/Client/ClientTableRow.vue';
|
||||||
import ClientCreateModal from '@/Components/Common/Client/ClientCreateModal.vue';
|
import ClientCreateModal from '@/Components/Common/Client/ClientCreateModal.vue';
|
||||||
import ClientTableHeading from '@/Components/Common/Client/ClientTableHeading.vue';
|
import ClientTableHeading from '@/Components/Common/Client/ClientTableHeading.vue';
|
||||||
|
import { canCreateClients } from '@/utils/permissions';
|
||||||
|
|
||||||
const { clients } = storeToRefs(useClientsStore());
|
const { clients } = storeToRefs(useClientsStore());
|
||||||
|
|
||||||
@@ -29,8 +30,11 @@ const createClient = ref(false);
|
|||||||
<UserCircleIcon
|
<UserCircleIcon
|
||||||
class="w-8 text-icon-default inline pb-2"></UserCircleIcon>
|
class="w-8 text-icon-default inline pb-2"></UserCircleIcon>
|
||||||
<h3 class="text-white font-semibold">No clients found</h3>
|
<h3 class="text-white font-semibold">No clients found</h3>
|
||||||
<p class="pb-5">Create your first client now!</p>
|
<p class="pb-5" v-if="canCreateClients()">
|
||||||
|
Create your first client now!
|
||||||
|
</p>
|
||||||
<SecondaryButton
|
<SecondaryButton
|
||||||
|
v-if="canCreateClients()"
|
||||||
@click="createClient = true"
|
@click="createClient = true"
|
||||||
:icon="PlusIcon"
|
:icon="PlusIcon"
|
||||||
>Create your First Client
|
>Create your First Client
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import Dropdown from '@/Components/Dropdown.vue';
|
import Dropdown from '@/Components/Dropdown.vue';
|
||||||
import { TrashIcon } from '@heroicons/vue/20/solid';
|
import { TrashIcon } from '@heroicons/vue/20/solid';
|
||||||
import type { Member } from '@/utils/api';
|
import type { Member } from '@/utils/api';
|
||||||
|
import { canDeleteMembers } from '@/utils/permissions';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
delete: [];
|
delete: [];
|
||||||
@@ -31,6 +32,7 @@ const props = defineProps<{
|
|||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<button
|
<button
|
||||||
|
v-if="canDeleteMembers()"
|
||||||
@click="emit('delete')"
|
@click="emit('delete')"
|
||||||
:aria-label="'Delete Member ' + props.member.name"
|
:aria-label="'Delete Member ' + props.member.name"
|
||||||
data-testid="client_delete"
|
data-testid="client_delete"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import SecondaryButton from '@/Components/SecondaryButton.vue';
|
|||||||
import { api } from '../../../../../openapi.json.client';
|
import { api } from '../../../../../openapi.json.client';
|
||||||
import { getCurrentOrganizationId } from '@/utils/useUser';
|
import { getCurrentOrganizationId } from '@/utils/useUser';
|
||||||
import { useNotificationsStore } from '@/utils/notification';
|
import { useNotificationsStore } from '@/utils/notification';
|
||||||
|
import { canInvitePlaceholderMembers } from '@/utils/permissions';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
member: Member;
|
member: Member;
|
||||||
@@ -70,7 +71,10 @@ async function invitePlaceholder(id: string) {
|
|||||||
<div
|
<div
|
||||||
class="relative whitespace-nowrap flex items-center pl-3 text-right text-sm font-medium sm:pr-0 pr-4 sm:pr-6 lg:pr-8 3xl:pr-12">
|
class="relative whitespace-nowrap flex items-center pl-3 text-right text-sm font-medium sm:pr-0 pr-4 sm:pr-6 lg:pr-8 3xl:pr-12">
|
||||||
<SecondaryButton
|
<SecondaryButton
|
||||||
v-if="member.is_placeholder === true"
|
v-if="
|
||||||
|
member.is_placeholder === true &&
|
||||||
|
canInvitePlaceholderMembers()
|
||||||
|
"
|
||||||
@click="invitePlaceholder(member.id)"
|
@click="invitePlaceholder(member.id)"
|
||||||
size="small"
|
size="small"
|
||||||
>Invite</SecondaryButton
|
>Invite</SecondaryButton
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import Dropdown from '@/Components/Dropdown.vue';
|
import Dropdown from '@/Components/Dropdown.vue';
|
||||||
import { TrashIcon, PencilSquareIcon } from '@heroicons/vue/20/solid';
|
import { TrashIcon, PencilSquareIcon } from '@heroicons/vue/20/solid';
|
||||||
import type { Project } from '@/utils/api';
|
import type { Project } from '@/utils/api';
|
||||||
|
import { canDeleteProjects, canUpdateProjects } from '@/utils/permissions';
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
delete: [];
|
delete: [];
|
||||||
edit: [];
|
edit: [];
|
||||||
@@ -34,12 +35,14 @@ const props = defineProps<{
|
|||||||
@click.prevent="emit('delete')"
|
@click.prevent="emit('delete')"
|
||||||
:aria-label="'Delete Project ' + props.project.name"
|
:aria-label="'Delete Project ' + props.project.name"
|
||||||
data-testid="project_delete"
|
data-testid="project_delete"
|
||||||
|
v-if="canDeleteProjects()"
|
||||||
class="border-b border-card-background-separator flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out">
|
class="border-b border-card-background-separator flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out">
|
||||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||||
<span>Delete</span>
|
<span>Delete</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@click.prevent="emit('edit')"
|
@click.prevent="emit('edit')"
|
||||||
|
v-if="canUpdateProjects()"
|
||||||
:aria-label="'Edit Project ' + props.project.name"
|
:aria-label="'Edit Project ' + props.project.name"
|
||||||
data-testid="project_edit"
|
data-testid="project_edit"
|
||||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out">
|
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out">
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import ProjectCreateModal from '@/Components/Common/Project/ProjectCreateModal.v
|
|||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import ProjectTableHeading from '@/Components/Common/Project/ProjectTableHeading.vue';
|
import ProjectTableHeading from '@/Components/Common/Project/ProjectTableHeading.vue';
|
||||||
import ProjectTableRow from '@/Components/Common/Project/ProjectTableRow.vue';
|
import ProjectTableRow from '@/Components/Common/Project/ProjectTableRow.vue';
|
||||||
|
import { canCreateProjects } from '@/utils/permissions';
|
||||||
|
|
||||||
const { projects } = storeToRefs(useProjectsStore());
|
const { projects } = storeToRefs(useProjectsStore());
|
||||||
|
|
||||||
@@ -29,8 +30,11 @@ const createProject = ref(false);
|
|||||||
<FolderPlusIcon
|
<FolderPlusIcon
|
||||||
class="w-8 text-icon-default inline pb-2"></FolderPlusIcon>
|
class="w-8 text-icon-default inline pb-2"></FolderPlusIcon>
|
||||||
<h3 class="text-white font-semibold">No projects found</h3>
|
<h3 class="text-white font-semibold">No projects found</h3>
|
||||||
<p class="pb-5">Create your first project now!</p>
|
<p class="pb-5" v-if="canCreateProjects()">
|
||||||
|
Create your first project now!
|
||||||
|
</p>
|
||||||
<SecondaryButton
|
<SecondaryButton
|
||||||
|
v-if="canCreateProjects()"
|
||||||
@click="createProject = true"
|
@click="createProject = true"
|
||||||
:icon="PlusIcon"
|
:icon="PlusIcon"
|
||||||
>Create your First Project
|
>Create your First Project
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useTagsStore } from '@/utils/useTags';
|
|||||||
import TagTableRow from '@/Components/Common/Tag/TagTableRow.vue';
|
import TagTableRow from '@/Components/Common/Tag/TagTableRow.vue';
|
||||||
import TagCreateModal from '@/Components/Common/Tag/TagCreateModal.vue';
|
import TagCreateModal from '@/Components/Common/Tag/TagCreateModal.vue';
|
||||||
import TagTableHeading from '@/Components/Common/Tag/TagTableHeading.vue';
|
import TagTableHeading from '@/Components/Common/Tag/TagTableHeading.vue';
|
||||||
|
import { canCreateTags } from '@/utils/permissions';
|
||||||
|
|
||||||
const { tags } = storeToRefs(useTagsStore());
|
const { tags } = storeToRefs(useTagsStore());
|
||||||
const createTag = ref(false);
|
const createTag = ref(false);
|
||||||
@@ -28,8 +29,13 @@ const createTag = ref(false);
|
|||||||
<FolderPlusIcon
|
<FolderPlusIcon
|
||||||
class="w-8 text-icon-default inline pb-2"></FolderPlusIcon>
|
class="w-8 text-icon-default inline pb-2"></FolderPlusIcon>
|
||||||
<h3 class="text-white font-semibold">No tags found</h3>
|
<h3 class="text-white font-semibold">No tags found</h3>
|
||||||
<p class="pb-5">Create your first tag now!</p>
|
<p class="pb-5" v-if="canCreateTags()">
|
||||||
<SecondaryButton @click="createTag = true" :icon="PlusIcon"
|
Create your first tag now!
|
||||||
|
</p>
|
||||||
|
<SecondaryButton
|
||||||
|
v-if="canCreateTags()"
|
||||||
|
@click="createTag = true"
|
||||||
|
:icon="PlusIcon"
|
||||||
>Create your First Tag</SecondaryButton
|
>Create your First Tag</SecondaryButton
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { Tag } from '@/utils/api';
|
|||||||
import { useTagsStore } from '@/utils/useTags';
|
import { useTagsStore } from '@/utils/useTags';
|
||||||
import TagMoreOptionsDropdown from '@/Components/Common/Tag/TagMoreOptionsDropdown.vue';
|
import TagMoreOptionsDropdown from '@/Components/Common/Tag/TagMoreOptionsDropdown.vue';
|
||||||
import TableRow from '@/Components/TableRow.vue';
|
import TableRow from '@/Components/TableRow.vue';
|
||||||
|
import { canDeleteTags } from '@/utils/permissions';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
tag: Tag;
|
tag: Tag;
|
||||||
@@ -24,6 +25,7 @@ function deleteTag() {
|
|||||||
<div
|
<div
|
||||||
class="relative whitespace-nowrap flex items-center pl-3 text-right text-sm font-medium sm:pr-0 pr-4 sm:pr-6 lg:pr-8 3xl:pr-12">
|
class="relative whitespace-nowrap flex items-center pl-3 text-right text-sm font-medium sm:pr-0 pr-4 sm:pr-6 lg:pr-8 3xl:pr-12">
|
||||||
<TagMoreOptionsDropdown
|
<TagMoreOptionsDropdown
|
||||||
|
v-if="canDeleteTags()"
|
||||||
:tag="tag"
|
:tag="tag"
|
||||||
@delete="deleteTag"></TagMoreOptionsDropdown>
|
@delete="deleteTag"></TagMoreOptionsDropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useTasksStore } from '@/utils/useTasks';
|
|||||||
import TaskTableRow from '@/Components/Common/Task/TaskTableRow.vue';
|
import TaskTableRow from '@/Components/Common/Task/TaskTableRow.vue';
|
||||||
import TaskTableHeading from '@/Components/Common/Task/TaskTableHeading.vue';
|
import TaskTableHeading from '@/Components/Common/Task/TaskTableHeading.vue';
|
||||||
import TaskCreateModal from '@/Components/Common/Task/TaskCreateModal.vue';
|
import TaskCreateModal from '@/Components/Common/Task/TaskCreateModal.vue';
|
||||||
|
import { canCreateTasks } from '@/utils/permissions';
|
||||||
|
|
||||||
const { tasks } = storeToRefs(useTasksStore());
|
const { tasks } = storeToRefs(useTasksStore());
|
||||||
|
|
||||||
@@ -39,8 +40,13 @@ const createTask = ref(false);
|
|||||||
<PlusCircleIcon
|
<PlusCircleIcon
|
||||||
class="w-8 text-icon-default inline pb-2"></PlusCircleIcon>
|
class="w-8 text-icon-default inline pb-2"></PlusCircleIcon>
|
||||||
<h3 class="text-white font-semibold">No tasks found</h3>
|
<h3 class="text-white font-semibold">No tasks found</h3>
|
||||||
<p class="pb-5">Create your first task now!</p>
|
<p class="pb-5" v-if="canCreateTasks()">
|
||||||
<SecondaryButton @click="createTask = true" :icon="PlusIcon"
|
Create your first task now!
|
||||||
|
</p>
|
||||||
|
<SecondaryButton
|
||||||
|
v-if="canCreateTasks()"
|
||||||
|
@click="createTask = true"
|
||||||
|
:icon="PlusIcon"
|
||||||
>Create your First Task
|
>Create your First Task
|
||||||
</SecondaryButton>
|
</SecondaryButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { router, usePage } from '@inertiajs/vue3';
|
|||||||
import type { Organization, User } from '@/types/models';
|
import type { Organization, User } from '@/types/models';
|
||||||
import { isBillingActivated } from '@/utils/billing';
|
import { isBillingActivated } from '@/utils/billing';
|
||||||
import { initializeStores } from '@/utils/init';
|
import { initializeStores } from '@/utils/init';
|
||||||
|
import { canUpdateOrganization } from '@/utils/permissions';
|
||||||
|
|
||||||
const page = usePage<{
|
const page = usePage<{
|
||||||
jetstream: {
|
jetstream: {
|
||||||
@@ -88,7 +89,9 @@ const switchToTeam = (team: Organization) => {
|
|||||||
Organization Settings
|
Organization Settings
|
||||||
</DropdownLink>
|
</DropdownLink>
|
||||||
|
|
||||||
<DropdownLink v-if="isBillingActivated()" href="/billing">
|
<DropdownLink
|
||||||
|
v-if="canUpdateOrganization() && isBillingActivated()"
|
||||||
|
href="/billing">
|
||||||
Billing
|
Billing
|
||||||
</DropdownLink>
|
</DropdownLink>
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,12 @@ import MainContainer from '@/Pages/MainContainer.vue';
|
|||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import NotificationContainer from '@/Components/NotificationContainer.vue';
|
import NotificationContainer from '@/Components/NotificationContainer.vue';
|
||||||
import { initializeStores } from '@/utils/init';
|
import { initializeStores } from '@/utils/init';
|
||||||
|
import {
|
||||||
|
canViewClients,
|
||||||
|
canViewMembers,
|
||||||
|
canViewProjects,
|
||||||
|
canViewTags,
|
||||||
|
} from '@/utils/permissions';
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
title: String,
|
title: String,
|
||||||
@@ -84,6 +90,7 @@ onMounted(async () => {
|
|||||||
<nav>
|
<nav>
|
||||||
<ul class="space-y-1">
|
<ul class="space-y-1">
|
||||||
<NavigationSidebarItem
|
<NavigationSidebarItem
|
||||||
|
v-if="canViewProjects()"
|
||||||
title="Projects"
|
title="Projects"
|
||||||
:icon="FolderIcon"
|
:icon="FolderIcon"
|
||||||
:href="route('projects')"
|
:href="route('projects')"
|
||||||
@@ -91,16 +98,19 @@ onMounted(async () => {
|
|||||||
route().current('projects')
|
route().current('projects')
|
||||||
"></NavigationSidebarItem>
|
"></NavigationSidebarItem>
|
||||||
<NavigationSidebarItem
|
<NavigationSidebarItem
|
||||||
|
v-if="canViewClients()"
|
||||||
title="Clients"
|
title="Clients"
|
||||||
:icon="UserCircleIcon"
|
:icon="UserCircleIcon"
|
||||||
:current="route().current('clients')"
|
:current="route().current('clients')"
|
||||||
:href="route('clients')"></NavigationSidebarItem>
|
:href="route('clients')"></NavigationSidebarItem>
|
||||||
<NavigationSidebarItem
|
<NavigationSidebarItem
|
||||||
title="Members"
|
title="Members"
|
||||||
|
v-if="canViewMembers()"
|
||||||
:icon="UserGroupIcon"
|
:icon="UserGroupIcon"
|
||||||
:current="route().current('members')"
|
:current="route().current('members')"
|
||||||
:href="route('members')"></NavigationSidebarItem>
|
:href="route('members')"></NavigationSidebarItem>
|
||||||
<NavigationSidebarItem
|
<NavigationSidebarItem
|
||||||
|
v-if="canViewTags()"
|
||||||
title="Tags"
|
title="Tags"
|
||||||
:icon="TagIcon"
|
:icon="TagIcon"
|
||||||
:current="route().current('tags')"
|
:current="route().current('tags')"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { useClientsStore } from '@/utils/useClients';
|
|||||||
import ClientTable from '@/Components/Common/Client/ClientTable.vue';
|
import ClientTable from '@/Components/Common/Client/ClientTable.vue';
|
||||||
import ClientCreateModal from '@/Components/Common/Client/ClientCreateModal.vue';
|
import ClientCreateModal from '@/Components/Common/Client/ClientCreateModal.vue';
|
||||||
import PageTitle from '@/Components/Common/PageTitle.vue';
|
import PageTitle from '@/Components/Common/PageTitle.vue';
|
||||||
|
import { canCreateClients } from '@/utils/permissions';
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
useClientsStore().fetchClients();
|
useClientsStore().fetchClients();
|
||||||
@@ -31,7 +32,10 @@ const createClient = ref(false);
|
|||||||
<TabBarItem>Archived</TabBarItem>
|
<TabBarItem>Archived</TabBarItem>
|
||||||
</TabBar>
|
</TabBar>
|
||||||
</div>
|
</div>
|
||||||
<SecondaryButton :icon="PlusIcon" @click="createClient = true"
|
<SecondaryButton
|
||||||
|
v-if="canCreateClients()"
|
||||||
|
:icon="PlusIcon"
|
||||||
|
@click="createClient = true"
|
||||||
>Create Client</SecondaryButton
|
>Create Client</SecondaryButton
|
||||||
>
|
>
|
||||||
<ClientCreateModal v-model:show="createClient"></ClientCreateModal>
|
<ClientCreateModal v-model:show="createClient"></ClientCreateModal>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import MemberInviteModal from '@/Components/Common/Member/MemberInviteModal.vue'
|
|||||||
import type { Role } from '@/types/jetstream';
|
import type { Role } from '@/types/jetstream';
|
||||||
import PageTitle from '@/Components/Common/PageTitle.vue';
|
import PageTitle from '@/Components/Common/PageTitle.vue';
|
||||||
import InvitationTable from '@/Components/Common/Invitation/InvitationTable.vue';
|
import InvitationTable from '@/Components/Common/Invitation/InvitationTable.vue';
|
||||||
|
import { canCreateInvitations } from '@/utils/permissions';
|
||||||
|
|
||||||
const inviteMember = ref(false);
|
const inviteMember = ref(false);
|
||||||
|
|
||||||
@@ -44,7 +45,10 @@ function isActiveTab(tab: string) {
|
|||||||
>
|
>
|
||||||
</TabBar>
|
</TabBar>
|
||||||
</div>
|
</div>
|
||||||
<SecondaryButton :icon="PlusIcon" @click="inviteMember = true"
|
<SecondaryButton
|
||||||
|
v-if="canCreateInvitations()"
|
||||||
|
:icon="PlusIcon"
|
||||||
|
@click="inviteMember = true"
|
||||||
>Invite member</SecondaryButton
|
>Invite member</SecondaryButton
|
||||||
>
|
>
|
||||||
<MemberInviteModal
|
<MemberInviteModal
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import Card from '@/Components/Common/Card.vue';
|
|||||||
import ProjectMemberTable from '@/Components/Common/ProjectMember/ProjectMemberTable.vue';
|
import ProjectMemberTable from '@/Components/Common/ProjectMember/ProjectMemberTable.vue';
|
||||||
import ProjectMemberCreateModal from '@/Components/Common/ProjectMember/ProjectMemberCreateModal.vue';
|
import ProjectMemberCreateModal from '@/Components/Common/ProjectMember/ProjectMemberCreateModal.vue';
|
||||||
import { useProjectMembersStore } from '@/utils/useProjectMembers';
|
import { useProjectMembersStore } from '@/utils/useProjectMembers';
|
||||||
|
import { canCreateTasks, canViewProjectMembers } from '@/utils/permissions';
|
||||||
|
|
||||||
const { projects } = storeToRefs(useProjectsStore());
|
const { projects } = storeToRefs(useProjectsStore());
|
||||||
|
|
||||||
@@ -83,6 +84,7 @@ onMounted(() => {
|
|||||||
<CardTitle title="Tasks" :icon="CheckCircleIcon">
|
<CardTitle title="Tasks" :icon="CheckCircleIcon">
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<SecondaryButton
|
<SecondaryButton
|
||||||
|
v-if="canCreateTasks()"
|
||||||
:icon="PlusIcon"
|
:icon="PlusIcon"
|
||||||
@click="createTask = true"
|
@click="createTask = true"
|
||||||
>Create Task
|
>Create Task
|
||||||
@@ -96,7 +98,7 @@ onMounted(() => {
|
|||||||
<TaskTable :project-id="projectId"></TaskTable>
|
<TaskTable :project-id="projectId"></TaskTable>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div v-if="canViewProjectMembers()">
|
||||||
<CardTitle title="Project Members" :icon="UserGroupIcon">
|
<CardTitle title="Project Members" :icon="UserGroupIcon">
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<SecondaryButton
|
<SecondaryButton
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { onMounted, ref } from 'vue';
|
|||||||
import { useProjectsStore } from '@/utils/useProjects';
|
import { useProjectsStore } from '@/utils/useProjects';
|
||||||
import ProjectCreateModal from '@/Components/Common/Project/ProjectCreateModal.vue';
|
import ProjectCreateModal from '@/Components/Common/Project/ProjectCreateModal.vue';
|
||||||
import PageTitle from '@/Components/Common/PageTitle.vue';
|
import PageTitle from '@/Components/Common/PageTitle.vue';
|
||||||
|
import { canCreateProjects } from '@/utils/permissions';
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
useProjectsStore().fetchProjects();
|
useProjectsStore().fetchProjects();
|
||||||
@@ -30,7 +31,10 @@ const createProject = ref(false);
|
|||||||
<TabBarItem>Archived</TabBarItem>
|
<TabBarItem>Archived</TabBarItem>
|
||||||
</TabBar>
|
</TabBar>
|
||||||
</div>
|
</div>
|
||||||
<SecondaryButton :icon="PlusIcon" @click="createProject = true"
|
<SecondaryButton
|
||||||
|
v-if="canCreateProjects()"
|
||||||
|
:icon="PlusIcon"
|
||||||
|
@click="createProject = true"
|
||||||
>Create Project</SecondaryButton
|
>Create Project</SecondaryButton
|
||||||
>
|
>
|
||||||
<ProjectCreateModal
|
<ProjectCreateModal
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { ref } from 'vue';
|
|||||||
import TagTable from '@/Components/Common/Tag/TagTable.vue';
|
import TagTable from '@/Components/Common/Tag/TagTable.vue';
|
||||||
import TagCreateModal from '@/Components/Common/Tag/TagCreateModal.vue';
|
import TagCreateModal from '@/Components/Common/Tag/TagCreateModal.vue';
|
||||||
import PageTitle from '@/Components/Common/PageTitle.vue';
|
import PageTitle from '@/Components/Common/PageTitle.vue';
|
||||||
|
import { canCreateTags } from '@/utils/permissions';
|
||||||
|
|
||||||
const createTag = ref(false);
|
const createTag = ref(false);
|
||||||
</script>
|
</script>
|
||||||
@@ -18,7 +19,10 @@ const createTag = ref(false);
|
|||||||
<div class="flex items-center space-x-6">
|
<div class="flex items-center space-x-6">
|
||||||
<PageTitle :icon="FolderIcon" title="Tags"> </PageTitle>
|
<PageTitle :icon="FolderIcon" title="Tags"> </PageTitle>
|
||||||
</div>
|
</div>
|
||||||
<SecondaryButton :icon="PlusIcon" @click="createTag = true"
|
<SecondaryButton
|
||||||
|
v-if="canCreateTags()"
|
||||||
|
:icon="PlusIcon"
|
||||||
|
@click="createTag = true"
|
||||||
>Create Tag</SecondaryButton
|
>Create Tag</SecondaryButton
|
||||||
>
|
>
|
||||||
<TagCreateModal v-model:show="createTag"></TagCreateModal>
|
<TagCreateModal v-model:show="createTag"></TagCreateModal>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import type { Organization } from '@/types/models';
|
|||||||
import type { Permissions } from '@/types/jetstream';
|
import type { Permissions } from '@/types/jetstream';
|
||||||
import { CreditCardIcon } from '@heroicons/vue/20/solid';
|
import { CreditCardIcon } from '@heroicons/vue/20/solid';
|
||||||
import { isBillingActivated } from '@/utils/billing';
|
import { isBillingActivated } from '@/utils/billing';
|
||||||
|
import { canUpdateOrganization } from '@/utils/permissions';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
team: Organization;
|
team: Organization;
|
||||||
@@ -63,7 +64,9 @@ const updateTeamName = () => {
|
|||||||
<Link href="/billing">
|
<Link href="/billing">
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
type="button"
|
type="button"
|
||||||
v-if="isBillingActivated()">
|
v-if="
|
||||||
|
isBillingActivated() && canUpdateOrganization()
|
||||||
|
">
|
||||||
<CreditCardIcon class="w-5 h-5 me-2" />
|
<CreditCardIcon class="w-5 h-5 me-2" />
|
||||||
Go to Billing
|
Go to Billing
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import UpdateTeamNameForm from '@/Pages/Teams/Partials/UpdateTeamNameForm.vue';
|
|||||||
import type { Organization } from '@/types/models';
|
import type { Organization } from '@/types/models';
|
||||||
import type { Permissions, Role } from '@/types/jetstream';
|
import type { Permissions, Role } from '@/types/jetstream';
|
||||||
import ImportData from '@/Pages/Teams/Partials/ImportData.vue';
|
import ImportData from '@/Pages/Teams/Partials/ImportData.vue';
|
||||||
|
import { canUpdateOrganization } from '@/utils/permissions';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
team: Organization;
|
team: Organization;
|
||||||
@@ -42,7 +43,9 @@ defineProps<{
|
|||||||
|
|
||||||
<SectionBorder />
|
<SectionBorder />
|
||||||
|
|
||||||
<ImportData :team="team"></ImportData>
|
<ImportData
|
||||||
|
v-if="canUpdateOrganization()"
|
||||||
|
:team="team"></ImportData>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
|
|||||||
@@ -5,13 +5,18 @@ import { useCurrentTimeEntryStore } from '@/utils/useCurrentTimeEntry';
|
|||||||
import { useClientsStore } from '@/utils/useClients';
|
import { useClientsStore } from '@/utils/useClients';
|
||||||
import { useMembersStore } from '@/utils/useMembers';
|
import { useMembersStore } from '@/utils/useMembers';
|
||||||
import { useTimeEntriesStore } from '@/utils/useTimeEntries';
|
import { useTimeEntriesStore } from '@/utils/useTimeEntries';
|
||||||
|
import { canViewClients, canViewMembers } from '@/utils/permissions';
|
||||||
|
|
||||||
export function initializeStores() {
|
export function initializeStores() {
|
||||||
useProjectsStore().fetchProjects();
|
useProjectsStore().fetchProjects();
|
||||||
useTasksStore().fetchTasks();
|
useTasksStore().fetchTasks();
|
||||||
useTagsStore().fetchTags();
|
useTagsStore().fetchTags();
|
||||||
useCurrentTimeEntryStore().fetchCurrentTimeEntry();
|
useCurrentTimeEntryStore().fetchCurrentTimeEntry();
|
||||||
useClientsStore().fetchClients();
|
if (canViewMembers()) {
|
||||||
useMembersStore().fetchMembers();
|
useMembersStore().fetchMembers();
|
||||||
|
}
|
||||||
|
if (canViewClients()) {
|
||||||
|
useClientsStore().fetchClients();
|
||||||
|
}
|
||||||
useTimeEntriesStore().fetchTimeEntries();
|
useTimeEntriesStore().fetchTimeEntries();
|
||||||
}
|
}
|
||||||
|
|||||||
83
resources/js/utils/permissions.ts
Normal file
83
resources/js/utils/permissions.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { usePage } from '@inertiajs/vue3';
|
||||||
|
|
||||||
|
const page = usePage<{
|
||||||
|
auth: {
|
||||||
|
permissions: string[];
|
||||||
|
};
|
||||||
|
}>();
|
||||||
|
|
||||||
|
function currentUserHasPermission(permission: string) {
|
||||||
|
console.log(page.props.auth.permissions);
|
||||||
|
if (Array.isArray(page.props.auth.permissions)) {
|
||||||
|
return page.props.auth.permissions.includes(permission);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canUpdateOrganization() {
|
||||||
|
return currentUserHasPermission('organizations:update');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canViewProjects() {
|
||||||
|
return currentUserHasPermission('projects:view');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canCreateProjects() {
|
||||||
|
return currentUserHasPermission('projects:create');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canUpdateProjects() {
|
||||||
|
return currentUserHasPermission('projects:update');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canDeleteProjects() {
|
||||||
|
return currentUserHasPermission('projects:delete');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canViewProjectMembers() {
|
||||||
|
return currentUserHasPermission('project-members:view');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canCreateTasks() {
|
||||||
|
return currentUserHasPermission('tasks:create');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canCreateClients() {
|
||||||
|
return currentUserHasPermission('clients:create');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canDeleteClients() {
|
||||||
|
return currentUserHasPermission('clients:delete');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canViewClients() {
|
||||||
|
return currentUserHasPermission('clients:view');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canViewMembers() {
|
||||||
|
return currentUserHasPermission('members:view');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canDeleteMembers() {
|
||||||
|
return currentUserHasPermission('members:delete');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canInvitePlaceholderMembers() {
|
||||||
|
return currentUserHasPermission('members:invite-placeholder');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canCreateInvitations() {
|
||||||
|
return currentUserHasPermission('invitations:create');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canViewTags() {
|
||||||
|
return currentUserHasPermission('tags:view');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canCreateTags() {
|
||||||
|
return currentUserHasPermission('tags:create');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canDeleteTags() {
|
||||||
|
return currentUserHasPermission('tags:delete');
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user