refactor(ui): teams

This commit is contained in:
liyasthomas
2021-08-03 16:50:28 +05:30
parent 7d913b0ee7
commit 17192c898b
10 changed files with 208 additions and 173 deletions

View File

@@ -118,7 +118,7 @@ a {
} }
.popover-theme { .popover-theme {
@apply bg-primary; @apply bg-popover;
@apply text-secondary; @apply text-secondary;
@apply p-2; @apply p-2;
@apply shadow-lg; @apply shadow-lg;
@@ -126,19 +126,19 @@ a {
} }
&[x-placement^="top"] .tippy-tooltip .tippy-arrow { &[x-placement^="top"] .tippy-tooltip .tippy-arrow {
@apply border-t-primary; @apply border-t-popover;
} }
&[x-placement^="bottom"] .tippy-tooltip .tippy-arrow { &[x-placement^="bottom"] .tippy-tooltip .tippy-arrow {
@apply border-b-primary; @apply border-b-popover;
} }
&[x-placement^="left"] .tippy-tooltip .tippy-arrow { &[x-placement^="left"] .tippy-tooltip .tippy-arrow {
@apply border-l-primary; @apply border-l-popover;
} }
&[x-placement^="right"] .tippy-tooltip .tippy-arrow { &[x-placement^="right"] .tippy-tooltip .tippy-arrow {
@apply border-r-primary; @apply border-r-popover;
} }
} }

View File

@@ -27,6 +27,8 @@
--error-color: theme("colors.dark.800"); --error-color: theme("colors.dark.800");
// Tooltip color // Tooltip color
--tooltip-color: theme("colors.true-gray.100"); --tooltip-color: theme("colors.true-gray.100");
// Popover color
--popover-color: theme("colors.true-gray.800");
// Editor theme // Editor theme
--editor-theme: "merbivore_soft"; --editor-theme: "merbivore_soft";
} }
@@ -54,6 +56,8 @@
--error-color: theme("colors.blue-gray.700"); --error-color: theme("colors.blue-gray.700");
// Tooltip color // Tooltip color
--tooltip-color: theme("colors.blue-gray.800"); --tooltip-color: theme("colors.blue-gray.800");
// Popover color
--popover-color: theme("colors.white");
// Editor theme // Editor theme
--editor-theme: "textmate"; --editor-theme: "textmate";
} }
@@ -81,6 +85,8 @@
--error-color: theme("colors.dark.800"); --error-color: theme("colors.dark.800");
// Tooltip color // Tooltip color
--tooltip-color: theme("colors.true-gray.100"); --tooltip-color: theme("colors.true-gray.100");
// Popover color
--popover-color: theme("colors.dark.700");
// Editor theme // Editor theme
--editor-theme: "twilight"; --editor-theme: "twilight";
} }

View File

@@ -41,7 +41,8 @@
'<br>' + '<br>' +
`<sub>${currentUser.email || 'Email not found'}</sub>` `<sub>${currentUser.email || 'Email not found'}</sub>`
" "
:indicator="isOnLine ? 'bg-green-500' : 'bg-red-500'" indicator
:indicator-styles="isOnLine ? 'bg-green-500' : 'bg-red-500'"
/> />
<TabPrimary <TabPrimary
v-else v-else

View File

@@ -27,10 +27,11 @@
transition transition
w-28 w-28
truncate truncate
focus:outline-none focus:border-accent focus:border-accent focus:outline-none
" "
:value="newMethod" :value="newMethod"
readonly :readonly="isCustomMethod()"
@input="onSelectMethod($event.target.value)"
/> />
</template> </template>
<SmartItem <SmartItem
@@ -78,7 +79,7 @@
px-4 px-4
transition transition
truncate truncate
focus:outline-none focus:border-accent focus:border-accent focus:outline-none
" "
name="url" name="url"
type="text" type="text"
@@ -332,6 +333,7 @@ export default defineComponent({
updateMethod(methods[currentIndex - 1]) updateMethod(methods[currentIndex - 1])
} }
} }
const cycleDownMethod = () => { const cycleDownMethod = () => {
const currentIndex = methods.indexOf(newMethod.value) const currentIndex = methods.indexOf(newMethod.value)
if (currentIndex === -1) { if (currentIndex === -1) {
@@ -363,6 +365,12 @@ export default defineComponent({
defineActionHandler("request.method.delete", () => updateMethod("DELETE")) defineActionHandler("request.method.delete", () => updateMethod("DELETE"))
defineActionHandler("request.method.head", () => updateMethod("HEAD")) defineActionHandler("request.method.head", () => updateMethod("HEAD"))
const isCustomMethod = () => {
if (newMethod.value === "CUSTOM" || !methods.includes(newMethod.value))
return false
return true
}
return { return {
newEndpoint, newEndpoint,
newMethod, newMethod,
@@ -386,6 +394,8 @@ export default defineComponent({
methodOptions, methodOptions,
sendOptions, sendOptions,
saveOptions, saveOptions,
isCustomMethod,
} }
}, },
}) })

View File

@@ -16,9 +16,10 @@
/> />
<div class="rounded-full shadow-inner inset-0 absolute"></div> <div class="rounded-full shadow-inner inset-0 absolute"></div>
<span <span
v-if="indicator"
:class="[ :class="[
'border-primary rounded-full border-2 h-3 -top-1 -right-1 w-3 absolute', 'border-primary rounded-full border-2 h-3 -top-1 -right-1 w-3 absolute',
indicator, indicatorStyles,
]" ]"
></span> ></span>
</div> </div>
@@ -38,6 +39,10 @@ export default {
default: "Profile picture", default: "Profile picture",
}, },
indicator: { indicator: {
type: Boolean,
default: false,
},
indicatorStyles: {
type: String, type: String,
default: "bg-green-500", default: "bg-green-500",
}, },

View File

@@ -2,9 +2,7 @@
<SmartModal v-if="show" @close="hideModal"> <SmartModal v-if="show" @close="hideModal">
<template #header> <template #header>
<h3 class="heading">{{ $t("team.edit") }}</h3> <h3 class="heading">{{ $t("team.edit") }}</h3>
<div> <ButtonSecondary icon="close" @click.native="hideModal" />
<ButtonSecondary icon="close" @click.native="hideModal" />
</div>
</template> </template>
<template #body> <template #body>
<div class="flex flex-col px-2"> <div class="flex flex-col px-2">
@@ -58,7 +56,7 @@
/> />
<span class="select-wrapper"> <span class="select-wrapper">
<tippy <tippy
ref="options" ref="memberOptions"
interactive interactive
tabindex="-1" tabindex="-1"
trigger="click" trigger="click"
@@ -89,22 +87,22 @@
<SmartItem <SmartItem
label="OWNER" label="OWNER"
@click.native=" @click.native="
updateRole(index, 'OWNER') $refs.memberOptions.tippy().hide()
$refs.options.tippy().hide() updateMemberRole(index, 'OWNER')
" "
/> />
<SmartItem <SmartItem
label="EDITOR" label="EDITOR"
@click.native=" @click.native="
updateRole(index, 'EDITOR') $refs.memberOptions.tippy().hide()
$refs.options.tippy().hide() updateMemberRole(index, 'EDITOR')
" "
/> />
<SmartItem <SmartItem
label="VIEWER" label="VIEWER"
@click.native=" @click.native="
updateRole(index, 'VIEWER') $refs.memberOptions.tippy().hide()
$refs.options.tippy().hide() updateMemberRole(index, 'VIEWER')
" "
/> />
</tippy> </tippy>
@@ -122,7 +120,7 @@
</div> </div>
<div <div
v-for="(member, index) in newMembers" v-for="(member, index) in newMembers"
:key="`member-${index}`" :key="`new-member-${index}`"
class=" class="
divide-x divide-dividerLight divide-x divide-dividerLight
border-b border-dividerLight border-b border-dividerLight
@@ -146,7 +144,7 @@
/> />
<span class="select-wrapper"> <span class="select-wrapper">
<tippy <tippy
ref="memberOptions" ref="newMemberOptions"
interactive interactive
tabindex="-1" tabindex="-1"
trigger="click" trigger="click"
@@ -177,22 +175,22 @@
<SmartItem <SmartItem
label="OWNER" label="OWNER"
@click.native=" @click.native="
member.value = 'OWNER' $refs.newMemberOptions.tippy().hide()
$refs.memberOptions.tippy().hide() updateNewMemberRole(index, 'OWNER')
" "
/> />
<SmartItem <SmartItem
label="EDITOR" label="EDITOR"
@click.native=" @click.native="
member.value = 'EDITOR' $refs.newMemberOptions.tippy().hide()
$refs.memberOptions.tippy().hide() updateNewMemberRole(index, 'EDITOR')
" "
/> />
<SmartItem <SmartItem
label="VIEWER" label="VIEWER"
@click.native=" @click.native="
member.value = 'VIEWER' $refs.newMemberOptions.tippy().hide()
$refs.memberOptions.tippy().hide() updateNewMemberRole(index, 'VIEWER')
" "
/> />
</tippy> </tippy>
@@ -222,10 +220,11 @@
<script> <script>
import cloneDeep from "lodash/cloneDeep" import cloneDeep from "lodash/cloneDeep"
import { defineComponent } from "@nuxtjs/composition-api"
import * as teamUtils from "~/helpers/teams/utils" import * as teamUtils from "~/helpers/teams/utils"
import TeamMemberAdapter from "~/helpers/teams/TeamMemberAdapter" import TeamMemberAdapter from "~/helpers/teams/TeamMemberAdapter"
export default { export default defineComponent({
props: { props: {
show: Boolean, show: Boolean,
editingTeam: { type: Object, default: () => {} }, editingTeam: { type: Object, default: () => {} },
@@ -263,12 +262,15 @@ export default {
}) })
}, },
methods: { methods: {
updateRole(id, role) { updateMemberRole(id, role) {
this.members[id].role = role this.members[id].role = role
}, },
updateNewMemberRole(id, role) {
this.newMembers[id].value = role
},
addTeamMember() { addTeamMember() {
const value = { key: "", value: "" } const member = { key: "", value: "" }
this.newMembers.push(value) this.newMembers.push(member)
}, },
removeExistingTeamMember(userID) { removeExistingTeamMember(userID) {
teamUtils teamUtils
@@ -409,5 +411,5 @@ export default {
this.$emit("hide-modal") this.$emit("hide-modal")
}, },
}, },
} })
</script> </script>

View File

@@ -1,64 +1,81 @@
<template> <template>
<div class="flex flex-1"> <div class="flex flex-1 items-end">
<div> <div class="flex flex-1 items-start">
<ButtonSecondary <div class="p-4">
v-tippy="{ theme: 'tooltip' }" <label
:title="team.myRole === 'OWNER' ? $t('edit') : ''" class="
icon="group" cursor-pointer
:label="team.name" font-semibold
@click.native="team.myRole === 'OWNER' ? $emit('edit-team') : ''" transition
/> hover:text-secondaryDark
"
@click="team.myRole === 'OWNER' ? $emit('edit-team') : ''"
>
{{ team.name || $t("nothing_found") }}
</label>
<div class="flex -space-x-1 mt-2 overflow-hidden">
<img
v-for="(member, index) in team.members"
:key="`member-${index}`"
:src="member.user.photoURL"
:alt="member.user.displayName"
class="rounded-full h-4 ring-primary ring-2 w-4 inline-block"
/>
</div>
</div>
</div> </div>
<tippy <span>
ref="options" <tippy
interactive ref="options"
tabindex="-1" interactive
trigger="click" tabindex="-1"
theme="popover" trigger="click"
arrow theme="popover"
> arrow
<template #trigger> >
<TabPrimary <template #trigger>
v-tippy="{ theme: 'tooltip' }" <ButtonSecondary
:title="$t('more')" v-tippy="{ theme: 'tooltip' }"
icon="more_vert" :title="$t('more')"
icon="more_vert"
/>
</template>
<SmartItem
v-if="team.myRole === 'OWNER'"
icon="create"
:label="$t('edit')"
@click.native="
$emit('edit-team')
$refs.options.tippy().hide()
"
/> />
</template> <SmartItem
<SmartItem v-if="team.myRole === 'OWNER'"
v-if="team.myRole === 'OWNER'" icon="delete"
icon="create" color="red"
:label="$t('edit')" :label="$t('delete')"
@click.native=" @click.native="
$emit('edit-team') deleteTeam
$refs.options.tippy().hide() $refs.options.tippy().hide()
" "
/> />
<SmartItem <SmartItem
v-if="team.myRole === 'OWNER'" v-tippy="{ theme: 'tooltip' }"
icon="delete" :title="
color="red" team.myRole === 'OWNER' && team.ownersCount == 1
:label="$t('delete')" ? $t('team.exit_disabled')
@click.native=" : ''
deleteTeam "
$refs.options.tippy().hide() :disabled="team.myRole === 'OWNER' && team.ownersCount == 1"
" icon="remove"
/> :label="$t('team.exit')"
<SmartItem @click.native="
v-tippy="{ theme: 'tooltip' }" exitTeam
:title=" $refs.options.tippy().hide()
team.myRole === 'OWNER' && team.ownersCount == 1 "
? $t('team.exit_disabled') />
: '' </tippy>
" </span>
:disabled="team.myRole === 'OWNER' && team.ownersCount == 1"
icon="remove"
:label="$t('team.exit')"
@click.native="
exitTeam
$refs.options.tippy().hide()
"
/>
</tippy>
</div> </div>
</template> </template>

View File

@@ -1,22 +1,44 @@
<template> <template>
<AppSection label="teams"> <AppSection label="teams">
<div class="flex flex-col"> <h4 class="font-bold text-secondaryDark">
<legend class="font-bold text-secondaryDark"> {{ $t("team.title") }}
{{ $t("team.title") }} </h4>
</legend> <div class="mt-1 text-secondaryLight">
<div v-if="currentUser"></div> Join
<div v-else> <SmartAnchor label="beta" to="https://hoppscotch.io/beta" blank />
<label>{{ $t("login_with") }}</label> to access teams.
<p> </div>
<ButtonPrimary <div class="space-y-4 mt-4">
v-if="currentUser" <ButtonSecondary
label="Get Started" :label="$t('team.create_new')"
@click.native="showLogin = true" outline
/> @click.native="displayModalAdd(true)"
</p> />
<p v-if="$apollo.queries.myTeams.loading">
{{ $t("loading") }}
</p>
<p v-if="myTeams.length === 0">
<i class="material-icons">help_outline</i> {{ $t("team.create_new") }}
</p>
<div
v-else
class="
divide-y divide-dividerLight
border border-divider
rounded
flex flex-col flex-1
md:w-64
"
>
<TeamsTeam
v-for="(team, index) in myTeams"
:key="`team-${index}`"
:team-i-d="team.id"
:team="team"
@edit-team="editTeam(team, team.id)"
/>
</div> </div>
</div> </div>
<TeamsAdd :show="showModalAdd" @hide-modal="displayModalAdd(false)" /> <TeamsAdd :show="showModalAdd" @hide-modal="displayModalAdd(false)" />
<TeamsEdit <TeamsEdit
:team="myTeams[0]" :team="myTeams[0]"
@@ -25,33 +47,6 @@
:editingteam-i-d="editingteamID" :editingteam-i-d="editingteamID"
@hide-modal="displayModalEdit(false)" @hide-modal="displayModalEdit(false)"
/> />
<div class="flex flex-1">
<div>
<ButtonSecondary
icon="add"
:label="$t('new')"
@click.native="displayModalAdd(true)"
/>
</div>
</div>
<p v-if="$apollo.queries.myTeams.loading">
{{ $t("loading") }}
</p>
<p v-if="myTeams.length === 0">
<i class="material-icons">help_outline</i> {{ $t("team.create_new") }}
</p>
<div v-else class="hide-scrollbar !overflow-auto">
<ul class="flex-col">
<li v-for="(team, index) in myTeams" :key="`team-${index}`">
<TeamsTeam
:team-i-d="team.id"
:team="team"
@edit-team="editTeam(team, team.id)"
/>
</li>
</ul>
</div>
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
</AppSection> </AppSection>
</template> </template>
@@ -68,7 +63,6 @@ export default {
editingteamID: "", editingteamID: "",
me: {}, me: {},
myTeams: [], myTeams: [],
showLogin: false,
} }
}, },
subscriptions() { subscriptions() {
@@ -98,6 +92,7 @@ export default {
ownersCount ownersCount
members { members {
user { user {
photoURL
displayName displayName
email email
uid uid

View File

@@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<div class="divide-y divide-dividerLight space-y-8"> <div class="divide-y divide-dividerLight space-y-8">
<div class="md:grid md:grid-cols-3 md:gap-4"> <div class="md:grid md:gap-4 md:grid-cols-3">
<div class="p-8 md:col-span-1"> <div class="p-8 md:col-span-1">
<h3 class="heading"> <h3 class="heading">
{{ $t("account") }} {{ $t("account") }}
@@ -16,8 +16,8 @@
<div class="mt-4 text-secondaryLight">Log in to access.</div> <div class="mt-4 text-secondaryLight">Log in to access.</div>
</div> </div>
<div v-else class="space-y-8"> <div v-else class="space-y-8">
<fieldset> <section>
<legend class="font-bold text-secondaryDark">User</legend> <h4 class="font-bold text-secondaryDark">User</h4>
<div class="space-y-4 mt-4"> <div class="space-y-4 mt-4">
<div class="flex items-start"> <div class="flex items-start">
<div class="flex items-center"> <div class="flex items-center">
@@ -29,7 +29,7 @@
<i v-else class="material-icons">account_circle</i> <i v-else class="material-icons">account_circle</i>
</div> </div>
<div class="ml-4"> <div class="ml-4">
<label> <label class="font-semibold">
{{ currentUser.displayName || $t("nothing_found") }} {{ currentUser.displayName || $t("nothing_found") }}
</label> </label>
<p class="mt-1 text-secondaryLight"> <p class="mt-1 text-secondaryLight">
@@ -42,7 +42,7 @@
<i class="material-icons">email</i> <i class="material-icons">email</i>
</div> </div>
<div class="ml-4"> <div class="ml-4">
<label> <label class="font-semibold">
{{ currentUser.email || $t("nothing_found") }} {{ currentUser.email || $t("nothing_found") }}
</label> </label>
<p class="mt-1 text-secondaryLight"> <p class="mt-1 text-secondaryLight">
@@ -51,9 +51,10 @@
</div> </div>
</div> </div>
</div> </div>
</fieldset> </section>
<fieldset> <Teams v-if="currentBackendUser && currentBackendUser.eaInvited" />
<legend class="font-bold text-secondaryDark">Sync</legend> <section>
<h4 class="font-bold text-secondaryDark">Sync</h4>
<div class="mt-1 text-secondaryLight"> <div class="mt-1 text-secondaryLight">
These settings are synced to cloud. These settings are synced to cloud.
</div> </div>
@@ -87,15 +88,12 @@
</SmartToggle> </SmartToggle>
</div> </div>
</div> </div>
</fieldset> </section>
<fieldset v-if="currentBackendUser && currentBackendUser.eaInvited">
<Teams />
</fieldset>
</div> </div>
</div> </div>
</div> </div>
<div class="md:grid md:grid-cols-3 md:gap-4"> <div class="md:grid md:gap-4 md:grid-cols-3">
<div class="p-8 md:col-span-1"> <div class="p-8 md:col-span-1">
<h3 class="heading"> <h3 class="heading">
{{ $t("theme") }} {{ $t("theme") }}
@@ -105,10 +103,10 @@
</p> </p>
</div> </div>
<div class="space-y-8 p-8 md:col-span-2"> <div class="space-y-8 p-8 md:col-span-2">
<fieldset> <section>
<legend class="font-bold text-secondaryDark"> <h4 class="font-bold text-secondaryDark">
{{ $t("background") }} {{ $t("background") }}
</legend> </h4>
<div class="mt-1 text-secondaryLight"> <div class="mt-1 text-secondaryLight">
<ColorScheme placeholder="..." tag="span"> <ColorScheme placeholder="..." tag="span">
{{ {{
@@ -123,30 +121,30 @@
<div class="mt-4"> <div class="mt-4">
<SmartColorModePicker /> <SmartColorModePicker />
</div> </div>
</fieldset> </section>
<fieldset> <section>
<legend class="font-bold text-secondaryDark"> <h4 class="font-bold text-secondaryDark">
{{ $t("color") }} {{ $t("color") }}
</legend> </h4>
<div class="mt-1 text-secondaryLight"> <div class="mt-1 text-secondaryLight">
{{ active.charAt(0).toUpperCase() + active.slice(1) }} {{ active.charAt(0).toUpperCase() + active.slice(1) }}
</div> </div>
<div class="mt-4"> <div class="mt-4">
<SmartAccentModePicker /> <SmartAccentModePicker />
</div> </div>
</fieldset> </section>
<fieldset> <section>
<legend class="font-bold text-secondaryDark"> <h4 class="font-bold text-secondaryDark">
{{ $t("choose_language") }} {{ $t("choose_language") }}
</legend> </h4>
<div class="mt-4"> <div class="mt-4">
<SmartChangeLanguage /> <SmartChangeLanguage />
</div> </div>
</fieldset> </section>
<fieldset> <section>
<legend class="font-bold text-secondaryDark"> <h4 class="font-bold text-secondaryDark">
{{ $t("experiments") }} {{ $t("experiments") }}
</legend> </h4>
<div class="mt-1 text-secondaryLight"> <div class="mt-1 text-secondaryLight">
{{ $t("experiments_notice") }} {{ $t("experiments_notice") }}
<SmartLink <SmartLink
@@ -205,11 +203,11 @@
</SmartToggle> </SmartToggle>
</div> </div>
</div> </div>
</fieldset> </section>
</div> </div>
</div> </div>
<div class="md:grid md:grid-cols-3 md:gap-4"> <div class="md:grid md:gap-4 md:grid-cols-3">
<div class="p-8 md:col-span-1"> <div class="p-8 md:col-span-1">
<h3 class="heading"> <h3 class="heading">
{{ $t("settings.interceptor") }} {{ $t("settings.interceptor") }}
@@ -219,10 +217,10 @@
</p> </p>
</div> </div>
<div class="space-y-8 p-8 md:col-span-2"> <div class="space-y-8 p-8 md:col-span-2">
<fieldset> <section>
<legend class="font-bold text-secondaryDark"> <h4 class="font-bold text-secondaryDark">
{{ $t("extensions") }} {{ $t("extensions") }}
</legend> </h4>
<div class="mt-1 text-secondaryLight"> <div class="mt-1 text-secondaryLight">
<span v-if="extensionVersion != null"> <span v-if="extensionVersion != null">
{{ {{
@@ -266,11 +264,11 @@
</SmartToggle> </SmartToggle>
</div> </div>
</div> </div>
</fieldset> </section>
<fieldset> <section>
<legend class="font-bold text-secondaryDark"> <h4 class="font-bold text-secondaryDark">
{{ $t("proxy") }} {{ $t("proxy") }}
</legend> </h4>
<div class="mt-1 text-secondaryLight"> <div class="mt-1 text-secondaryLight">
{{ `${$t("official_proxy_hosting")} ${$t("read_the")}` }} {{ `${$t("official_proxy_hosting")} ${$t("read_the")}` }}
<SmartLink <SmartLink
@@ -315,7 +313,7 @@
py-2 py-2
px-4 px-4
block block
focus:outline-none focus:border-accent focus:border-accent focus:outline-none
" "
type="url" type="url"
:disabled="!PROXY_ENABLED" :disabled="!PROXY_ENABLED"
@@ -330,7 +328,7 @@
/> />
</div> </div>
</div> </div>
</fieldset> </section>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -22,6 +22,7 @@ export default defineConfig({
dividerDark: "var(--divider-dark-color)", dividerDark: "var(--divider-dark-color)",
error: "var(--error-color)", error: "var(--error-color)",
tooltip: "var(--tooltip-color)", tooltip: "var(--tooltip-color)",
popover: "var(--popover-color)",
gradientFrom: "var(--gradient-from-color)", gradientFrom: "var(--gradient-from-color)",
gradientVia: "var(--gradient-via-color)", gradientVia: "var(--gradient-via-color)",
gradientTo: "var(--gradient-to-color)", gradientTo: "var(--gradient-to-color)",