add responsive tables and time/client/member/tags index page

This commit is contained in:
Gregor Vostrak
2024-04-13 03:19:22 +02:00
parent 335a057ba1
commit 38535142b2
31 changed files with 83 additions and 95 deletions

View File

@@ -3,7 +3,7 @@ import { PLAYWRIGHT_BASE_URL } from '../playwright/config';
async function goToOrganizationSettings(page) {
await page.goto(PLAYWRIGHT_BASE_URL + '/dashboard');
await page.getByTestId('organization_switcher').click();
await page.locator('[data-testid="organization_switcher"]:visible').click();
await page.getByText('Team Settings').click();
}
@@ -12,7 +12,7 @@ test('test that organization name can be updated', async ({ page }) => {
await page.getByLabel('Team Name').fill('NEW ORG NAME');
await page.getByLabel('Team Name').press('Enter');
await page.getByLabel('Team Name').press('Meta+r');
await expect(page.getByTestId('organization_switcher')).toContainText(
await expect(page.locator('[data-testid="organization_switcher"]:visible')).toContainText(
'NEW ORG NAME'
);
});

View File

@@ -8,7 +8,7 @@ defineProps<{
</script>
<template>
<div class="flex w-full items-center justify-between pb-4">
<div class="flex w-full items-center justify-between pb-2.5 sm:pb-4">
<h3
class="text-white font-bold text-base flex items-center space-x-2.5">
<component

View File

@@ -16,7 +16,7 @@ const createClient = ref(false);
<template>
<ClientCreateModal v-model:show="createClient"></ClientCreateModal>
<div class="flow-root">
<div class="flow-root max-w-[100vw] overflow-x-auto">
<div class="inline-block min-w-full align-middle">
<div
data-testid="client_table"

View File

@@ -5,12 +5,10 @@ import TableHeading from '@/Components/Common/TableHeading.vue';
<template>
<TableHeading>
<div
class="py-1.5 pr-3 text-left text-sm font-semibold text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
class="py-1.5 pr-3 text-left font-semibold text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
Name
</div>
<div class="px-3 py-1.5 text-left text-sm font-semibold text-white">
Status
</div>
<div class="px-3 py-1.5 text-left font-semibold text-white">Status</div>
<div class="relative py-1.5 pl-3 pr-4 sm:pr-6 lg:pr-8 3xl:pr-12">
<span class="sr-only">Edit</span>
</div>

View File

@@ -8,7 +8,10 @@ defineProps<{
<template>
<div class="flex items-center space-x-2">
<svg class="w-5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<svg
class="w-4 sm:w-5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<g fill="none">
<path
d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035c-.01-.004-.019-.001-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427c-.002-.01-.009-.017-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093c.012.004.023 0 .029-.008l.004-.014l-.034-.614c-.003-.012-.01-.02-.02-.022m-.715.002a.023.023 0 0 0-.027.006l-.006.014l-.034.614c0 .012.007.02.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />

View File

@@ -12,7 +12,7 @@ const createClient = ref(false);
<template>
<ClientCreateModal v-model:show="createClient"></ClientCreateModal>
<div class="flow-root">
<div class="flow-root max-w-[100vw] overflow-x-auto">
<div class="inline-block min-w-full align-middle">
<div
data-testid="client_table"

View File

@@ -5,21 +5,15 @@ import TableHeading from '@/Components/Common/TableHeading.vue';
<template>
<TableHeading>
<div
class="py-1.5 pr-3 text-left text-sm font-semibold text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
class="py-1.5 pr-3 text-left font-semibold text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
Name
</div>
<div class="px-3 py-1.5 text-left text-sm font-semibold text-white">
Email
</div>
<div class="px-3 py-1.5 text-left text-sm font-semibold text-white">
Role
</div>
<div class="px-3 py-1.5 text-left text-sm font-semibold text-white">
<div class="px-3 py-1.5 text-left font-semibold text-white">Email</div>
<div class="px-3 py-1.5 text-left font-semibold text-white">Role</div>
<div class="px-3 py-1.5 text-left font-semibold text-white">
Billable Rate
</div>
<div class="px-3 py-1.5 text-left text-sm font-semibold text-white">
Status
</div>
<div class="px-3 py-1.5 text-left font-semibold text-white">Status</div>
<div
class="relative py-1.5 pl-3 pr-4 sm:pr-6 lg:pr-8 3xl:pr-12 bg-row-heading-background">
<span class="sr-only">Edit</span>

View File

@@ -0,0 +1,18 @@
<script setup lang="ts">
import type { Component } from 'vue';
defineProps<{
icon: Component;
title: string;
}>();
</script>
<template>
<h3
class="text-white font-bold text-sm sm:text-base flex items-center space-x-2 sm:space-x-2.5">
<component :is="icon" class="w-5 sm:w-6 text-icon-default"></component>
<span> {{ title }} </span>
</h3>
</template>
<style scoped></style>

View File

@@ -16,7 +16,7 @@ const createProject = ref(false);
<template>
<ProjectCreateModal v-model:show="createProject"></ProjectCreateModal>
<div class="flow-root">
<div class="flow-root max-w-[100vw] overflow-x-auto">
<div class="inline-block min-w-full align-middle">
<div
data-testid="project_table"

View File

@@ -5,18 +5,14 @@ import TableHeading from '@/Components/Common/TableHeading.vue';
<template>
<TableHeading>
<div
class="py-1.5 pr-3 text-left text-sm font-semibold text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
class="py-1.5 pr-3 text-left font-semibold text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
Name
</div>
<div class="px-3 py-1.5 text-left text-sm font-semibold text-white">
Client
</div>
<div class="px-3 py-1.5 text-left text-sm font-semibold text-white">
<div class="px-3 py-1.5 text-left font-semibold text-white">Client</div>
<div class="px-3 py-1.5 text-left font-semibold text-white">
Billable Rate
</div>
<div class="px-3 py-1.5 text-left text-sm font-semibold text-white">
Status
</div>
<div class="px-3 py-1.5 text-left font-semibold text-white">Status</div>
<div class="relative py-1.5 pl-3 pr-4 sm:pr-6 lg:pr-8 3xl:pr-12">
<span class="sr-only">Edit</span>
</div>

View File

@@ -5,15 +5,13 @@ import TableHeading from '@/Components/Common/TableHeading.vue';
<template>
<TableHeading>
<div
class="py-1.5 pr-3 text-left text-sm font-semibold text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
class="py-1.5 pr-3 text-left font-semibold text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
Name
</div>
<div class="px-3 py-1.5 text-left text-sm font-semibold text-white">
<div class="px-3 py-1.5 text-left font-semibold text-white">
Billable Rate
</div>
<div class="px-3 py-1.5 text-left text-sm font-semibold text-white">
Role
</div>
<div class="px-3 py-1.5 text-left font-semibold text-white">Role</div>
<div class="relative py-1.5 pl-3 pr-4 sm:pr-6 lg:pr-8 3xl:pr-12">
<span class="sr-only">Edit</span>
</div>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"></script>
<template>
<div class="flex items-center space-x-1">
<div class="flex items-center space-x-0.5 sm:space-x-1">
<slot></slot>
</div>
</template>

View File

@@ -18,7 +18,7 @@ const activeClass = computed(() => {
<button
:class="
twMerge(
'rounded-md transition px-3 py-1.5 text-sm font-medium hover:text-white',
'rounded-md transition px-2 sm:px-3 py-1 sm:py-1.5 text-xs sm:text-sm font-medium hover:text-white',
activeClass
)
">

View File

@@ -2,7 +2,7 @@
<template>
<div
class="contents [&>*]:border-row-separator [&>*]:border-b [&>*]:border-t [&>*]:bg-row-heading-background">
class="contents [&>*]:border-row-separator text-xs sm:text-sm [&>*]:border-b [&>*]:border-t [&>*]:bg-row-heading-background">
<slot></slot>
</div>
</template>

View File

@@ -5,7 +5,7 @@ import TableHeading from '@/Components/Common/TableHeading.vue';
<template>
<TableHeading>
<div
class="py-1.5 pr-3 text-left text-sm font-semibold text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
class="py-1.5 pr-3 text-left font-semibold text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
Name
</div>
<div class="relative py-1.5 pl-3 pr-4 sm:pr-6 lg:pr-8 3xl:pr-12">

View File

@@ -5,12 +5,10 @@ import TableHeading from '@/Components/Common/TableHeading.vue';
<template>
<TableHeading>
<div
class="py-1.5 pr-3 text-left text-sm font-semibold text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
class="py-1.5 pr-3 text-left font-semibold text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
Task Name
</div>
<div class="px-3 py-1.5 text-left text-sm font-semibold text-white">
Status
</div>
<div class="px-3 py-1.5 text-left font-semibold text-white">Status</div>
<div class="relative py-1.5 pl-3 pr-4 sm:pr-6 lg:pr-8 3xl:pr-12">
<span class="sr-only">Edit</span>
</div>

View File

@@ -82,13 +82,14 @@ function updateProjectAndTask(projectId: string, taskId: string) {
class="border-b border-card-border transition"
data-testid="time_entry_row">
<MainContainer>
<div class="flex py-1.5 items-center justify-between group">
<div class="sm:flex py-1.5 items-center justify-between group">
<div class="flex space-x-1 items-center">
<input
type="checkbox"
class="h-4 w-4 rounded bg-card-background border-input-border text-accent-500/80 focus:ring-accent-500/80" />
<TimeEntryDescriptionInput
@changed="updateTimeEntryDescription"
class="flex-1"
:modelValue="
timeEntry.description
"></TimeEntryDescriptionInput>
@@ -105,7 +106,7 @@ function updateProjectAndTask(projectId: string, taskId: string) {
<TimeEntryRowTagDropdown
@changed="updateTimeEntryTags"
:modelValue="timeEntry.tags"></TimeEntryRowTagDropdown>
<div>
<div class="flex-1">
<TimeEntryRangeSelector
:start="timeEntry.start"
:end="timeEntry.end"
@@ -122,7 +123,7 @@ function updateProjectAndTask(projectId: string, taskId: string) {
<TimeTrackerStartStop
@changed="onStartStopClick"
:active="!!(timeEntry.start && !timeEntry.end)"
class="opacity-20 group-hover:opacity-100"></TimeTrackerStartStop>
class="opacity-20 hidden sm:flex group-hover:opacity-100"></TimeTrackerStartStop>
<TimeEntryMoreOptionsDropdown
@delete="
deleteTimeEntry

View File

@@ -8,7 +8,7 @@ defineProps<{
<template>
<div
class="bg-card-background border-t border-b border-card-border py-1.5 text-sm">
class="bg-card-background border-t border-b border-card-border py-1.5 text-xs sm:text-sm">
<MainContainer>
<DaySectionHeader :date></DaySectionHeader>
</MainContainer>

View File

@@ -1,13 +1,7 @@
<template>
<section class="flex flex-col">
<h3
class="text-white font-bold pb-4 textbase flex items-center space-x-2.5">
<component
v-if="icon"
:is="icon"
class="w-6 text-icon-default"></component>
<span>{{ title }}</span>
</h3>
<CardTitle :title="title" :icon="icon"></CardTitle>
<div
class="rounded-lg bg-card-background border border-card-border flex-1 flex items-center">
<div class="divide-y divide-card-background-separator w-full">
@@ -19,6 +13,7 @@
<script setup lang="ts">
import type { Component } from 'vue';
import CardTitle from '@/Components/Common/CardTitle.vue';
defineProps<{
title: string;

View File

@@ -43,14 +43,15 @@ const switchToTeam = (team: Organization) => {
class="flex hover:bg-white/10 cursor-pointer transition px-2 py-1 rounded-lg w-full items-center justify-between font-medium">
<div class="flex flex-1 space-x-3 items-center w-4/5">
<div
class="rounded-lg bg-blue-900 font-semibold flex-shrink-0 text-white w-7 h-7 flex items-center justify-center">
class="rounded sm:rounded-lg bg-blue-900 font-semibold text-xs sm:text-base flex-shrink-0 text-white w-5 sm:w-7 h-5 sm:h-7 flex items-center justify-center">
{{
page.props.auth.user.current_team.name
.slice(0, 1)
.toUpperCase()
}}
</div>
<span class="text-lg flex-1 truncate font-semibold">
<span
class="text-sm sm:text-lg flex-1 truncate font-semibold">
{{ page.props.auth.user.current_team.name }}
</span>
</div>
@@ -58,7 +59,7 @@ const switchToTeam = (team: Organization) => {
<button
class="p-1 transition hover:bg-white/10 rounded-full flex items-center w-9 h-9">
<ChevronDownIcon
class="w-full mt-[1px]"></ChevronDownIcon>
class="w-5 sm:w-full mt-[1px]"></ChevronDownIcon>
</button>
</div>
</div>

View File

@@ -14,7 +14,7 @@ withDefaults(
<template>
<button
:type="type"
class="inline-flex items-center px-3 py-2 bg-accent-300/10 border border-accent-300/20 rounded-md font-medium text-sm text-white hover:bg-accent-300/20 focus:bg-white active:bg-accent-300/20 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150">
class="inline-flex items-center px-2 sm:px-3 py-1 sm:py-2 bg-accent-300/10 border border-accent-300/20 rounded-md font-medium text-xs sm:text-sm text-white hover:bg-accent-300/20 focus:bg-white active:bg-accent-300/20 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150">
<slot />
</button>
</template>

View File

@@ -17,7 +17,7 @@ const props = withDefaults(
<template>
<button
:type="type"
class="bg-button-secondary-background border border-button-secondary-border hover:bg-button-secondary-background-hover shadow-sm transition text-white text-sm px-3 py-2 rounded-lg font-semibold inline-flex items-center space-x-1.5 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-25 ease-in-out">
class="bg-button-secondary-background border border-button-secondary-border hover:bg-button-secondary-background-hover shadow-sm transition text-white text-xs sm:text-sm px-2 sm:px-3 py-1 sm:py-2 rounded-lg font-semibold inline-flex items-center space-x-1.5 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-25 ease-in-out">
<span
:class="
twMerge('flex items-center ', props.icon ? 'space-x-1.5' : '')
@@ -25,7 +25,7 @@ const props = withDefaults(
<component
v-if="props.icon"
:is="props.icon"
class="w-5 h-5 -ml-1"></component>
class="w-4 sm:w-5 h-4 sm:h-5 -ml-0.5 sm:-ml-1"></component>
<span>
<slot />
</span>

View File

@@ -166,7 +166,7 @@ function onTimeEntryEnterPress() {
type="text" />
</div>
<div class="flex items-center justify-between pl-2">
<div class="flex items-center w-[130px]">
<div class="flex items-center w-[130px] sm:w-auto">
<TimeTrackerProjectTaskDropdown
@changed="updateTimeEntry"
v-model:project="currentTimeEntry.project_id"
@@ -190,12 +190,12 @@ function onTimeEntryEnterPress() {
@blur="updateTimerAndStartLiveTimerUpdate"
@keydown.enter="onTimeEntryEnterPress"
v-model="currentTime"
class="w-[110px] sm:w-[130px] h-full text-white py-2.5 rounded-r-lg text-center px-4 text-lg font-bold bg-card-background border-none placeholder-muted focus:ring-0 transition focus:bg-card-background-active"
class="w-[110px] sm:w-[130px] h-full text-white py-2.5 rounded-r-lg text-center px-4 text-sm sm:text-lg font-bold bg-card-background border-none placeholder-muted focus:ring-0 transition focus:bg-card-background-active"
type="text" />
</div>
</div>
</div>
<div class="pl-6 pr-3 absolute sm:relative top-[7px] right-0">
<div class="pl-6 pr-3 absolute sm:relative top-[6px] sm:top-0 right-0">
<TimeTrackerStartStop
:active="isActive"
@changed="onToggleButtonPress"

View File

@@ -128,10 +128,10 @@ onMounted(async () => {
</div>
<div class="flex-1 sm:ml-[230px] 2xl:ml-[270px]">
<div
class="sm:hidden w-full px-3 py-3 border-b border-b-default-background-separator text-muted flex justify-between items-center">
class="sm:hidden w-full px-3 py-1 border-b border-b-default-background-separator text-muted flex justify-between items-center">
<Bars3Icon
@click="showSidebarMenu = !showSidebarMenu"
class="w-8 text-muted"></Bars3Icon>
class="w-7 text-muted"></Bars3Icon>
<OrganizationSwitcher></OrganizationSwitcher>
</div>

View File

@@ -10,6 +10,7 @@ import { onMounted, ref } from 'vue';
import { useClientsStore } from '@/utils/useClients';
import ClientTable from '@/Components/Common/Client/ClientTable.vue';
import ClientCreateModal from '@/Components/Common/Client/ClientCreateModal.vue';
import PageTitle from '@/Components/Common/PageTitle.vue';
onMounted(() => {
useClientsStore().fetchClients();
@@ -23,12 +24,7 @@ const createClient = ref(false);
<MainContainer
class="py-5 border-b border-default-background-separator flex justify-between items-center">
<div class="flex items-center space-x-6">
<h3
class="text-white font-bold text-base flex items-center space-x-2.5">
<UserCircleIcon
class="w-6 text-icon-default"></UserCircleIcon>
<span> Clients </span>
</h3>
<PageTitle :icon="UserCircleIcon" title="Clients"> </PageTitle>
<TabBar>
<TabBarItem>All</TabBarItem>
<TabBarItem active>Active</TabBarItem>

View File

@@ -50,11 +50,11 @@ const props = defineProps<{
<template>
<AppLayout title="Dashboard" data-testid="dashboard_view">
<MainContainer
class="pt-8 pb-6 border-b border-default-background-separator">
class="pt-5 sm:pt-8 pb-4 sm:pb-6 border-b border-default-background-separator">
<TimeTracker></TimeTracker>
</MainContainer>
<MainContainer
class="grid gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 pt-5 pb-6 border-b border-default-background-separator items-stretch">
class="grid gap-5 sm:gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 pt-3 sm:pt-5 pb-4 sm:pb-6 border-b border-default-background-separator items-stretch">
<RecentlyTrackedTasksCard
:latestTasks="props.latestTasks"></RecentlyTrackedTasksCard>
<LastSevenDaysCard

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"></script>
<template>
<div class="px-4 sm:px-6 lg:px-8 3xl:px-12 mx-auto">
<div class="px-3 sm:px-6 lg:px-8 3xl:px-12 mx-auto">
<slot></slot>
</div>
</template>

View File

@@ -10,6 +10,7 @@ import { ref } from 'vue';
import MemberTable from '@/Components/Common/Member/MemberTable.vue';
import MemberInviteModal from '@/Components/Common/Member/MemberInviteModal.vue';
import type { Role } from '@/types/jetstream';
import PageTitle from '@/Components/Common/PageTitle.vue';
const inviteMember = ref(false);
@@ -22,13 +23,8 @@ defineProps<{
<AppLayout title="Members" data-testid="members_view">
<MainContainer
class="py-5 border-b border-default-background-separator flex justify-between items-center">
<div class="flex items-center space-x-6">
<h3
class="text-white font-bold text-base flex items-center space-x-2.5">
<UserGroupIcon
class="w-6 text-icon-default"></UserGroupIcon>
<span> Members </span>
</h3>
<div class="flex items-center space-x-4 sm:space-x-6">
<PageTitle :icon="UserGroupIcon" title="Members"> </PageTitle>
<TabBar>
<TabBarItem active>All</TabBarItem>
<TabBarItem>Active</TabBarItem>

View File

@@ -9,6 +9,7 @@ import ProjectTable from '@/Components/Common/Project/ProjectTable.vue';
import { onMounted, ref } from 'vue';
import { useProjectsStore } from '@/utils/useProjects';
import ProjectCreateModal from '@/Components/Common/Project/ProjectCreateModal.vue';
import PageTitle from '@/Components/Common/PageTitle.vue';
onMounted(() => {
useProjectsStore().fetchProjects();
@@ -20,13 +21,9 @@ const createProject = ref(false);
<template>
<AppLayout title="Projects" data-testid="projects_view">
<MainContainer
class="py-5 border-b border-default-background-separator flex justify-between items-center">
<div class="flex items-center space-x-6">
<h3
class="text-white font-bold text-base flex items-center space-x-2.5">
<FolderIcon class="w-6 text-icon-default"></FolderIcon>
<span> Projects </span>
</h3>
class="py-3 sm:py-5 border-b border-default-background-separator flex justify-between items-center">
<div class="flex items-center space-x-3 sm:space-x-6">
<PageTitle :icon="FolderIcon" title="Projects"> </PageTitle>
<TabBar>
<TabBarItem>All</TabBarItem>
<TabBarItem active>Active</TabBarItem>

View File

@@ -6,6 +6,7 @@ import SecondaryButton from '@/Components/SecondaryButton.vue';
import { ref } from 'vue';
import TagTable from '@/Components/Common/Tag/TagTable.vue';
import TagCreateModal from '@/Components/Common/Tag/TagCreateModal.vue';
import PageTitle from '@/Components/Common/PageTitle.vue';
const createTag = ref(false);
</script>
@@ -15,11 +16,7 @@ const createTag = ref(false);
<MainContainer
class="py-5 border-b border-default-background-separator flex justify-between items-center">
<div class="flex items-center space-x-6">
<h3
class="text-white font-bold text-base flex items-center space-x-2.5">
<FolderIcon class="w-6 text-icon-default"></FolderIcon>
<span> Tags </span>
</h3>
<PageTitle :icon="FolderIcon" title="Tags"> </PageTitle>
</div>
<SecondaryButton :icon="PlusIcon" @click="createTag = true"
>Create Tag</SecondaryButton

View File

@@ -49,7 +49,7 @@ const groupedTimeEntries = computed(() => {
<template>
<AppLayout title="Dashboard" data-testid="time_view">
<MainContainer
class="py-8 border-b border-default-background-separator">
class="py-4 sm:py-8 border-b border-default-background-separator">
<TimeTracker></TimeTracker>
</MainContainer>
<div v-for="(value, key) in groupedTimeEntries" :key="key">