Skip to content

Redesign Teams tab with dropdown team selection, multi-line comparison charts, and bar chart visualizations #236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions app/components/MainComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@
<v-window v-show="(metricsReady && metrics.length) || (seatsReady && tab === 'seat analysis')" v-model="tab">
<v-window-item v-for="item in tabItems" :key="item" :value="item">
<v-card flat>
<MetricsViewer v-if="item === itemName" :metrics="metrics" :date-range-description="dateRangeDescription" />
<MetricsViewer v-if="item === getDisplayTabName(itemName)" :metrics="metrics" :date-range-description="dateRangeDescription" />
<TeamsComponent v-if="item === 'teams'" :date-range-description="dateRangeDescription" />
<BreakdownComponent
v-if="item === 'languages'" :metrics="metrics" :breakdown-key="'language'"
:date-range-description="dateRangeDescription" />
Expand Down Expand Up @@ -108,6 +109,7 @@ import MetricsViewer from './MetricsViewer.vue'
import BreakdownComponent from './BreakdownComponent.vue'
import CopilotChatViewer from './CopilotChatViewer.vue'
import SeatsAnalysisViewer from './SeatsAnalysisViewer.vue'
import TeamsComponent from './TeamsComponent.vue'
import ApiResponse from './ApiResponse.vue'
import AgentModeViewer from './AgentModeViewer.vue'
import DateRangeSelector from './DateRangeSelector.vue'
Expand All @@ -121,6 +123,7 @@ export default defineNuxtComponent({
BreakdownComponent,
CopilotChatViewer,
SeatsAnalysisViewer,
TeamsComponent,
ApiResponse,
AgentModeViewer,
DateRangeSelector
Expand All @@ -132,6 +135,19 @@ export default defineNuxtComponent({
this.seats = [];
clear();
},
getDisplayTabName(itemName: string): string {
// Transform scope names to display names for tabs
switch (itemName) {
case 'team-organization':
case 'team-enterprise':
return 'team';
case 'organization':
case 'enterprise':
return itemName;
default:
return itemName;
}
},
async handleDateRangeChange(newDateRange: {
since?: string;
until?: string;
Expand Down Expand Up @@ -232,7 +248,13 @@ export default defineNuxtComponent({
}
},
created() {
this.tabItems.unshift(this.itemName);
this.tabItems.unshift(this.getDisplayTabName(this.itemName));

// Add teams tab for organization and enterprise scopes to allow team comparison
if (this.itemName === 'organization' || this.itemName === 'enterprise') {
this.tabItems.splice(1, 0, 'teams'); // Insert after the first tab
}

this.config = useRuntimeConfig();
},
async mounted() {
Expand Down
34 changes: 27 additions & 7 deletions app/components/SeatsAnalysisViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ elevation="4" color="white" variant="elevated" class="mx-auto my-4"
<div v-bind="props" class="text-h6 mb-1">Total Assigned </div>
</template>
<v-card class="pa-2" style="background-color: #f0f0f0; max-width: 350px;">
<span class="text-caption" style="font-size: 10px !important;">This metric represents the total number of Copilot seats assigned within the current organization/enterprise.</span>
<span class="text-caption" style="font-size: 10px !important;">This metric represents the total number of Copilot seats assigned {{ isTeamView ? `to team "${currentTeam}"` : 'within the current organization/enterprise' }}.</span>
</v-card>
</v-tooltip>
<div class="text-caption">
Currently assigned seats
{{ isTeamView ? `Seats assigned to team "${currentTeam}"` : 'Currently assigned seats' }}
</div>
<p class="text-h4">{{ totalSeats.length }}</p>
</div>
Expand All @@ -33,7 +33,7 @@ elevation="4" color="white" variant="elevated" class="mx-auto my-3"
<div v-bind="props" class="text-h6 mb-1">Assigned But Never Used</div>
</template>
<v-card class="pa-2" style="background-color: #f0f0f0; max-width: 350px;">
<span class="text-caption" style="font-size: 10px !important;">This metric shows seats that were assigned but never used within the current organization/enterprise. The assigned timestamp is also displayed in the chart.</span>
<span class="text-caption" style="font-size: 10px !important;">This metric shows seats that were assigned but never used {{ isTeamView ? `within team "${currentTeam}"` : 'within the current organization/enterprise' }}. The assigned timestamp is also displayed in the chart.</span>
</v-card>
</v-tooltip>
<div class="text-caption">
Expand Down Expand Up @@ -108,7 +108,7 @@ elevation="4" color="white" variant="elevated" class="mx-auto my-3"
</template>

<script lang="ts">
import { defineComponent, ref, watchEffect } from 'vue';
import { defineComponent, ref, watchEffect, computed } from 'vue';
import type { Seat } from '@/model/Seat';
import {
Chart as ChartJS,
Expand Down Expand Up @@ -156,14 +156,27 @@ setup(props) {

watchEffect(() => {
if (props.seats && Array.isArray(props.seats)) {
totalSeats.value = props.seats;
// Filter seats by team if we're on a team-specific route
const config = useRuntimeConfig();
const route = useRoute();
let filteredSeats = props.seats;

// Check if we're on a team-specific URL
if (config.public.scope?.includes('team') && config.public.githubTeam) {
const teamName = config.public.githubTeam;
filteredSeats = props.seats.filter(seat =>
seat.team && seat.team.toLowerCase() === teamName.toLowerCase()
);
}

totalSeats.value = filteredSeats;

const oneWeekAgo = new Date();
const thirtyDaysAgo = new Date();
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

props.seats.forEach(seat => {
filteredSeats.forEach(seat => {
if(!seat.last_activity_at) {
noshowCount++;
} else {
Expand Down Expand Up @@ -197,11 +210,18 @@ setup(props) {
unusedSeatsInSevenDays.value = unusedIn7Count;
unusedSeatsInThirtyDays.value = unusedIn30Count;

// Add computed properties for team filtering info
const config = useRuntimeConfig();
const isTeamView = computed(() => config.public.scope?.includes('team') && config.public.githubTeam);
const currentTeam = computed(() => config.public.githubTeam || '');

return {
totalSeats,
noshowSeats: noshowSeats,
unusedSeatsInSevenDays: unusedSeatsInSevenDays,
unusedSeatsInThirtyDays: unusedSeatsInThirtyDays
unusedSeatsInThirtyDays: unusedSeatsInThirtyDays,
isTeamView,
currentTeam
}
},
data() {
Expand Down
Loading