Skip to content

Commit

Permalink
Add headshots ux (#145)
Browse files Browse the repository at this point in the history
  • Loading branch information
hyf-github-user authored Nov 4, 2024
1 parent d7592d8 commit fedee88
Show file tree
Hide file tree
Showing 36 changed files with 2,090 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add headshots ux",
"packageName": "@acedatacloud/nexior",
"email": "[email protected]",
"dependentChangeType": "patch"
}
19 changes: 17 additions & 2 deletions src/components/common/Navigator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ import {
ROUTE_QRART_HISTORY,
ROUTE_LUMA_INDEX,
ROUTE_LUMA_HISTORY,
ROUTE_HEADSHOTS_INDEX,
ROUTE_HEADSHOTS_HISTORY,
ROUTE_SUNO_INDEX,
ROUTE_SUNO_HISTORY,
ROUTE_SITE_INDEX
Expand Down Expand Up @@ -147,6 +149,7 @@ export default defineComponent({
computed: {
links() {
const result = [];
// Add chat's leftmost icon
if (this.$store?.state?.site?.features?.chat?.enabled) {
result.push({
route: {
Expand All @@ -157,6 +160,7 @@ export default defineComponent({
routes: [ROUTE_CHAT_CONVERSATION, ROUTE_CHAT_CONVERSATION_NEW]
});
}
// Add midjourney's leftmost icon
if (this.$store?.state?.site?.features?.midjourney?.enabled) {
result.push({
route: {
Expand All @@ -167,7 +171,7 @@ export default defineComponent({
routes: [ROUTE_MIDJOURNEY_INDEX]
});
}
// Add chatdoc's leftmost icon
/*
if (this.$store?.state?.site?.features?.chatdoc?.enabled) {
result.push({
Expand All @@ -180,7 +184,7 @@ export default defineComponent({
});
}
*/
// Add qrart's leftmost icon
if (this.$store?.state?.site?.features?.qrart?.enabled) {
result.push({
route: {
Expand Down Expand Up @@ -213,6 +217,17 @@ export default defineComponent({
routes: [ROUTE_LUMA_INDEX, ROUTE_LUMA_HISTORY]
});
}
// Add headshots's leftmost icon
if (this.$store?.state?.site?.features?.headshots?.enabled) {
result.push({
route: {
name: ROUTE_HEADSHOTS_INDEX
},
displayName: this.$t('common.nav.headshots'),
icon: 'fa-solid fa-id-card',
routes: [ROUTE_HEADSHOTS_INDEX, ROUTE_HEADSHOTS_HISTORY]
});
}
if (this.direction === 'row') {
result.push({
Expand Down
69 changes: 69 additions & 0 deletions src/components/headshots/ConfigPanel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<template>
<div class="panel">
<div class="config">
<elements-selector class="mb-4" />
<mode-selector class="mb-4" />
<image-urls-input class="mb-4" />
<div class="actions">
<el-button type="primary" class="btn w-full" round @click="onGenerate">
<font-awesome-icon icon="fa-solid fa-magic" class="mr-2" />
{{ $t('headshots.button.generate') }}
</el-button>
</div>
</div>
</div>
</template>

<script>
import { defineComponent } from 'vue';
import { ElButton } from 'element-plus';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import ImageUrlsInput from './config/ImageUrlsInput.vue';
// @ts-ignore
import ModeSelector from './config/ModeSelector.vue';
import ElementsSelector from './config/ElementsSelector.vue';
export default defineComponent({
name: 'PresetPanel',
components: {
ModeSelector,
ImageUrlsInput,
ElButton,
FontAwesomeIcon,
ElementsSelector
},
emits: ['generate'],
computed: {
config() {
return this.$store.state.headshots?.config;
}
},
methods: {
onGenerate() {
this.$emit('generate');
}
}
});
</script>
<style lang="scss" scoped>
.panel {
height: 100%;
padding: 15px;
display: flex;
flex-direction: column;
.config {
width: 100%;
height: calc(100% - 50px);
flex: 1;
}
.actions {
height: 50px;
display: flex;
justify-content: center;
align-items: center;
.btn {
width: 100%;
}
}
}
</style>
23 changes: 23 additions & 0 deletions src/components/headshots/DetailPanel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<template>
<div class="panel detail">
<task-detail />
</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import TaskDetail from './task/Detail.vue';
export default defineComponent({
name: 'DetailPanel',
components: {
TaskDetail
},
data() {
return {
job: 0
};
},
computed: {}
});
</script>
102 changes: 102 additions & 0 deletions src/components/headshots/ImageGallery.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<template>
<div class="image-gallery">
<div v-for="(image, index) in images" :key="index" class="image-container">
<img :src="image.image_url" alt="Image" />
<button class="view-button" @click="viewImage(image.image_url)">View Image</button>
</div>
</div>
</template>

<script lang="ts">
import { defineComponent, computed } from 'vue';
import { IHeadshotsTask } from '@/models';
interface IDataItem {
image_url: string;
// Add other properties if necessary
}
interface IResponse {
data: IDataItem[];
}
export default defineComponent({
name: 'ImageGallery',
props: {
modelValue: {
type: Object as () => IHeadshotsTask | undefined,
required: true
}
},
setup(props) {
// Computed property to extract the first two images
const images = computed(() => {
return props.modelValue?.response?.data?.slice(0, 2) || [];
});
// Method to handle the "View Image" button click
const viewImage = (url: string) => {
window.open(url, '_blank');
};
return {
images,
viewImage
};
}
});
</script>

<style lang="scss" scoped>
.image-gallery {
display: flex;
justify-content: space-between;
gap: 16px;
}
.image-container {
position: relative;
width: 48%; /* Ensures two images fit side by side with some space */
overflow: hidden;
border: 1px solid #ddd;
border-radius: 8px;
img {
width: 100%;
height: auto;
display: block;
transition: transform 0.3s ease;
}
/* Zoom effect on hover */
&:hover img {
transform: scale(1.05);
}
.view-button {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.6);
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
opacity: 0;
transition: opacity 0.3s ease;
/* Optional: Add a slight delay for smoother appearance */
transition-delay: 0.1s;
&:hover {
background-color: rgba(0, 0, 0, 0.8);
}
}
/* Show the button on hover */
&:hover .view-button {
opacity: 1;
}
}
</style>
77 changes: 77 additions & 0 deletions src/components/headshots/OperationPanel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<template>
<div>
<el-card v-show="operating">
<content-input class="mb-4" />
<prompt-input class="mb-4" />
<preset-selector class="mb-4" />
<div class="actions">
<el-button type="primary" class="btn w-full" round @click="onGenerate">
<font-awesome-icon icon="fa-solid fa-magic" class="mr-2" />
{{ $t('headshots.button.generate') }}
</el-button>
</div>
</el-card>
<div>
<el-button type="primary" class="btn btn-operate" @click="operating = !operating">+</el-button>
</div>
</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { ElButton, ElCard } from 'element-plus';
import PresetSelector from './config/PresetSelector2.vue';
import ContentInput from './config/ContentInput.vue';
import PromptInput from './config/PromptInput.vue';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
export default defineComponent({
name: 'OperationPanel',
components: {
ElButton,
ElCard,
PresetSelector,
ContentInput,
PromptInput,
FontAwesomeIcon
},
emits: ['generate'],
data() {
return {
operating: false
};
},
methods: {
onGenerate() {
this.$emit('generate');
this.operating = false;
}
}
});
</script>

<style lang="scss" scoped>
.el-card {
width: 580px;
height: fit-content;
overflow-y: scroll;
position: absolute;
bottom: 70px;
left: calc(50% - 300px);
@media (max-width: 767px) {
width: 100%;
left: 0;
}
}
.btn-operate {
width: 50px;
height: 50px;
border-radius: 50%;
font-size: 24px;
line-height: 40px;
padding: 0;
margin: auto;
display: block;
}
</style>
Loading

0 comments on commit fedee88

Please sign in to comment.