This commit is contained in:
min.jiang 2024-06-14 04:22:48 +08:00
parent 0140de1caa
commit b38f62799a
20 changed files with 565 additions and 473 deletions

View File

@ -14,7 +14,7 @@
"axios": "^1.4.0",
"bootstrap": "^5.3.1",
"buffer": "^6.0.3",
"pdfjs-dist": "3.5.141",
"pdfjs-dist": "^4.3.136",
"pinia": "^2.1.6",
"vue": "^3.3.4",
"vue-router": "^4.2.4",
@ -26,12 +26,12 @@
"@vue/eslint-config-prettier": "^8.0.0",
"eslint": "^8.45.0",
"eslint-plugin-vue": "^9.15.1",
"fast-glob": "^3.3.2",
"prettier": "^3.0.0",
"sass": "^1.66.1",
"sass-loader": "^13.3.2",
"vite": "^4.4.6",
"vite-plugin-svg-icons": "^2.0.1",
"fast-glob": "^3.3.2",
"webpack": "^5.88.2"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -1,40 +1,48 @@
.stop-button {
@extend .btn;
@extend .btn-secondary;
@extend .btn-outline-secondary;
@extend .btn-sm;
}
.back-button {
@extend .btn;
@extend .btn-secondary;
@extend .btn-outline-secondary;
@extend .btn-sm;
}
.proceed-button {
@extend .btn;
@extend .btn-primary;
@extend .btn-sm;
}
.initiate-button {
@extend .btn;
@extend .btn-primary;
@extend .btn-sm;
}
.proceed-button {
@extend .btn;
@extend .btn-primary;
@extend .btn-sm;
}
.option-button {
@extend .btn;
@extend .btn-primary;
@extend .btn-sm;
}
.close-button {
@extend .btn-close;
@extend .btn-sm;
}
.light-button {
@extend .btn;
@extend .btn-light;
@extend .btn-sm;
}
.inplace-proceed-button {
@ -44,3 +52,7 @@
.inplace-back-button {
@extend .light-button;
}
.min-btn {
min-width: 115px;
}

View File

@ -159,3 +159,41 @@ p {
}
}
}
.container {
@media (min-width: 1200px) {
max-width: $body-width;
}
}
.accordion-list {
box-shadow: 0px 0px 24px 0px #d4d3e380;
border: none;
border-radius: 12px;
margin-bottom: 16px;
.accordion-item {
border: none;
}
}
.accordion-button {
padding: 12px 28px 12px 12px;
outline: none;
box-shadow: none !important;
.dashed-container {
flex: 1;
margin-right: 28px;
padding: 8px 12px;
font-weight: bold;
border: 1px dashed #AEBFFD;
border-radius: 3px;
}
&:not(.collapsed) {
color: black;
background-color: transparent;
box-shadow: none;
border-bottom: 1px solid #dee2e6;
.dashed-container {
background-color: #F3F6FF;
}
}
}

View File

@ -1,10 +1,19 @@
<template>
<div class="freeleaps-editor">
<div v-if="!disabled" class="editor-control">
<div
class="editor-body"
:contenteditable="!disabled"
spellcheck="false"
ref="editor"
v-html="content"
@blur="updateAction"
@mouseup.stop="selectionChange"
/>
<div v-if="!disabled" class="editor-control" :style="editorCtrlStyle">
<div v-for="(item, index) in iconList" :key="index" class="editor-item">
<button
class="item-icon"
:class="{ activity: item.choose }"
:class="{ activity: commandStates.indexOf(item.type) !== -1, last: index === iconList.length - 1 }"
:data-info="item.name"
@click="iconClick($event, item.type)"
:data-bs-toggle="item.drop ? 'dropdown' : ''"
@ -76,14 +85,6 @@
</div>
</div>
</div>
<div
class="editor-body"
:contenteditable="!disabled"
spellcheck="false"
ref="editor"
v-html="content"
@blur="updateAction"
/>
</div>
</template>
<script>
@ -105,6 +106,8 @@ export default {
data() {
return {
selectedRange: '',
editorCtrlStyle: {},
commandStates: [],
iconList: [
// {
// name: 'lable', // hover name
@ -112,7 +115,6 @@ export default {
// icon: 'fe-paragraph', // icon style
// drop: true, // If there is drop menu
// canChoose: true, // chosen or not
// choose: false
// },
{
name: 'bold',
@ -120,7 +122,6 @@ export default {
icon: 'fe-bold',
drop: false,
canChoose: true,
choose: false
},
{
name: 'italic',
@ -128,7 +129,6 @@ export default {
icon: 'fe-italic',
drop: false,
canChoose: true,
choose: false
},
{
name: 'underline',
@ -136,7 +136,6 @@ export default {
icon: 'fe-underline',
drop: false,
canChoose: true,
choose: false
},
// {
// name: 'strike',
@ -144,7 +143,6 @@ export default {
// icon: 'fe-strike',
// drop: false,
// canChoose: true,
// choose: false
// },
// {
// name: 'clear-format',
@ -152,7 +150,6 @@ export default {
// icon: 'fe-clear',
// drop: false,
// canChoose: false,
// choose: false
// },
// {
// name: 'font-color',
@ -160,7 +157,6 @@ export default {
// icon: 'fe-char',
// drop: false,
// canChoose: true,
// choose: false
// },
{
name: 'unordered-list',
@ -168,7 +164,6 @@ export default {
icon: 'fe-unorderedlist',
drop: false,
canChoose: true,
choose: false
},
{
name: 'ordered list',
@ -176,7 +171,6 @@ export default {
icon: 'fe-orderedlist',
drop: false,
canChoose: true,
choose: false
}
// {
// name: 'align-justify',
@ -184,12 +178,41 @@ export default {
// icon: 'fe-alignjustify',
// drop: true,
// canChoose: true,
// choose: false
// }
]
}
},
methods: {
selectionChange(e) {
const sel = window.getSelection()
if (sel && sel.type === 'Range') {
// this.selectedRange = sel.getRangeAt(0)
// this.restoreSelection()
// console.log('window.getSelection',this.selectedRange)
// console.log('is bold ? ', this.queryCommandState(sel.getRangeAt(0), 'bold'))
// this.iconClick(null, 'bold')
this.editorCtrlStyle = { display: 'flex', top: `${e.layerY}px`, left: `${e.layerX}px` }
} else {
this.selectedRange = null
this.editorCtrlStyle = { display: 'none' }
// this.commandStates = []
}
},
queryCommandState(range, command) {
const container = document.createElement('span')
container.style.display = 'none';
container.appendChild(range.cloneContents())
console.log('queryCommandState', container)
// range
document.body.appendChild(container);
const state = document.queryCommandState(command);
console.log('queryCommandIndeterm ', document.queryCommandIndeterm('bold'))
console.log('queryCommandState ', document.queryCommandState('bold'))
// range.collapse(true);
container.remove();
return state;
},
iconClick(event, type, dropType) {
event.preventDefault()
this.$refs.editor.focus()
@ -200,26 +223,18 @@ export default {
if (dropType) {
type = dropType
}
let arr = []
arr = this.iconList.map((val) => {
if (type === val.type && val.canChoose) {
val.choose = val.choose ? false : true
}
return val
})
if (type === 'clear') {
arr = this.iconList.map((val) => {
val.choose = false
return val
})
const i = this.commandStates.indexOf(type)
if (i === -1) {
this.commandStates.push(type)
} else {
this.commandStates.splice(i,1)
}
this.iconList = arr
})
},
getSelect() {
if (window.getSelection) {
var sel = window.getSelection()
let sel = window.getSelection()
console.log('this.selectedRange',sel)
if (sel.rangeCount > 0) {
return sel.getRangeAt(0)
}
@ -272,9 +287,10 @@ export default {
default:
console.log('none')
}
// window.getSelection().removeAllRanges()
},
restoreSelection() {
var selection = window.getSelection()
let selection = window.getSelection()
if (this.selectedRange) {
try {
selection.removeAllRanges()
@ -297,9 +313,8 @@ export default {
<style lang="scss" scoped>
.freeleaps-editor {
position: relative;
min-width: 100px;
background-color: #fff;
border-radius: 4px;
border: 1px solid #a9a9a9;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
h1,
@ -330,15 +345,11 @@ export default {
font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
}
.editor-control {
display: flex;
flex-flow: row wrap;
min-height: 40px;
color: #333;
border-bottom: 1px solid transparent;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
background-color: #f5f5f5;
border-color: #ddd;
display: none;
width: fit-content;
height: 26px;
background-color: #F8F8F9;
position: absolute;
}
.dropmenu {
display: none;
@ -383,6 +394,7 @@ export default {
outline: none;
text-align: left;
border-radius: 4px;
position: relative;
p {
font-size: 14px;
color: #68747f;
@ -393,31 +405,32 @@ export default {
.editor-item {
position: relative;
vertical-align: middle;
border: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
cursor: pointer;
line-height: 20px;
border-radius: 3px;
margin: 4px 0 4px 5px;
background-color: white;
.item-icon {
position: relative;
display: inline-block;
width: 100%;
height: 100%;
padding: 2px 10px;
font-size: 18px;
width: 50px;
height: 16px;
font-size: 16px;
color: #9EA2AF;
font-weight: normal;
white-space: nowrap;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
user-select: none;
background-color: #fff;
border: 1px solid white;
outline: none;
transition: all 0.1s ease-out;
border-radius: 3px;
border: none;
line-height: 16px;
background: transparent;
border-right: 1px solid #E7E8EB;
&.last {
border-right: 0;
}
&::after {
position: absolute;
top: 0;
@ -457,9 +470,7 @@ export default {
}
&:hover,
&.activity {
color: #333;
background-color: #e6e6e6;
border-color: #e6e6e6;
color: black;
}
&:hover:after,
&:hover:before {

View File

@ -11,13 +11,17 @@
<script>
import * as PDFJS from 'pdfjs-dist/build/pdf'
import { WorksapceApi } from '@/utils/index'
PDFJS.GlobalWorkerOptions.workerSrc = import('pdfjs-dist/build/pdf.worker.entry')
PDFJS.GlobalWorkerOptions.workerPort = new Worker(
new URL("pdfjs-dist/build/pdf.worker.mjs", import.meta.url),
{ type: "module" }
)
// import * as PDFJS from 'pdfjs-dist/webpack.mjs'
export default {
name: 'PDFReader',
props: {
document: { type: String, default: '' }
doc: { type: Object, default: () => {} }
},
data() {
return {
@ -27,8 +31,12 @@ export default {
selectedPage: 1
}
},
mounted() {
this.renderPDF()
watch: {
doc() {
if (this.doc.url || this.doc.data) {
this.renderPDF()
}
}
},
methods: {
renderPage(num) {
@ -48,12 +56,8 @@ export default {
})
})
},
async renderPDF() {
const response = await WorksapceApi.fetchAttachedFileAsMediaData(
'66673218fa83335c3b1b11ec',
'6667321afa83335c3b1b11ee'
)
this.loadingTask = PDFJS.getDocument({ url: response.data })
renderPDF() {
this.loadingTask = PDFJS.getDocument(this.doc)
this.loadingTask.promise.then((pdf) => {
this.numPages = pdf.numPages
this.renderPage(1)

View File

@ -81,7 +81,6 @@ export default {
if (this.userIdentityNote.length > 8) {
this.userIdentityNote = this.userIdentityNote.slice(0, 5) + '...'
}
console.log('watch', this.$route.meta)
},
data() {
return {

View File

@ -0,0 +1 @@
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1362" width="64" height="64"><path d="M512 81.465C274.223 81.465 81.465 274.222 81.465 512S274.221 942.535 512 942.535c237.776 0 430.535-192.756 430.535-430.535S749.777 81.465 512 81.465z m0 804.279c-206.413 0-373.743-167.331-373.743-373.743S305.588 138.257 512 138.257s373.743 167.331 373.743 373.744S718.412 885.744 512 885.744z m58.193-364.989c4.018-8.407 6.272-17.821 6.272-27.76 0-24.174-13.313-45.226-33-56.26V248.949c0-6.781-5.498-12.28-12.28-12.28h-38.372c-6.781 0-12.28 5.498-12.28 12.28v187.785c-19.686 11.034-33 32.088-33 56.26 0 35.603 28.862 64.465 64.465 64.465a64.251 64.251 0 0 0 24.82-4.959l177.381 177.382c4.797 4.797 12.571 4.797 17.367 0l15.194-15.194c4.797-4.795 4.797-12.569 0-17.367L570.192 520.755z"></path></svg>

After

Width:  |  Height:  |  Size: 821 B

View File

@ -0,0 +1 @@
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1523" width="64" height="64"><path d="M758.94993735 933.875H265.05006265c-76.72843323 0-144.04975104-72.15625991-144.04975103-154.40283381V223.87422138C121.00031161 147.61877182 182.9085958 90.125 265.05006265 90.125H581.37091262l5.78090947 4.88749603c92.07411987 78.67292102 153.14154456 140.68631304 230.39551537 219.25412622 24.43748016 24.75280288 50.3990192 51.18732477 79.4612266 80.249533l5.99112432 6.04367824v378.91233271c-0.05255393 82.24657388-67.32131699 154.40283381-144.04975103 154.4028338zM265.05006265 131.2745639c-59.64847366 0-102.90018713 38.94230732-102.90018714 92.59965748v555.59794482c0 59.28059783 49.0326221 113.20071597 102.90018714 113.20071598h493.8998747c53.81501111 0 102.90018713-53.92011814 102.90018714-113.20071598V417.58723914c-26.80239771-26.74984461-50.87200286-51.29243262-73.62776404-74.46862354-74.9416072-76.15034253-134.53752695-136.74478354-222.14458055-211.89660562H265.05006265z" fill="#4D4D4D" p-id="1524"></path><path d="M861.85012449 460.52362989h-174.95133921c-82.19402078 0-154.35027988-67.32131699-154.35027988-144.04975105V131.2745639a20.6010585 20.6010585 0 0 1 41.14956391 1e-8v185.19931493c0 53.81501111 53.97267205 102.90018713 113.20071597 102.90018715h174.95133921a20.6010585 20.6010585 0 1 1 0 41.1495639z"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -14,8 +14,8 @@ export default {
mnx_navToContact() {
this.$router.push('/contact')
},
mnx_navToPdfContentViewer(content_id) {
this.$router.push('/pdf-content-viewer/' + content_id)
mnx_navToPdfContentViewer(request_id, document_id) {
this.$router.push(`/pdf-content-viewer?request_id=${request_id}&document_id=${document_id}`)
},
mnx_navToLinkContentViewer(content_link_based64) {
this.$router.push('/link-content-viewer/' + content_link_based64)

View File

@ -31,14 +31,14 @@
</div>
<button type="submit" class="btn-start">SIGN UP</button>
</div>
<p class="errorInput" v-if="message != null">{{ message }}</p>
<p class="error-msg" v-if="message != null">{{ message }}</p>
</div>
</form>
</div>
</template>
<script>
import { UserAuthApi, applicantValidator } from '../../utils/index'
import { UserAuthApi, passwordValidator } from '../../utils/index'
export default {
name: 'NewUserSetPassword',
props: {},
@ -67,8 +67,7 @@ export default {
this.message = 'The two passwords are not matched'
return
}
// this.message = applicantValidator.emailValidator.validate(this.password)
// console.log('EmailValidator', EmailValidator)
this.message = passwordValidator.validate(this.password)
if (this.message != null) return
UserAuthApi.updatePassword(this.password, this.password2)

View File

@ -1,47 +1,35 @@
<template>
<div class="pdf-viewer">
<PDFReader />
<PDFReader :doc="content_media_data" />
</div>
</template>
<script>
// import { pdfjsLib } from '../../plugins/index'
// import { ContentApi } from '../../utils/index'
// import { Buffer } from 'buffer'
import { WorksapceApi } from '@/utils/index'
import PDFReader from '@/components/PDFReader.vue'
export default {
name: 'PdfContentViewer',
props: {
content_id: {
required: true,
type: String
}
},
// props: {
// content_id: {
// required: true,
// type: String
// }
// },
components: { PDFReader },
computed: {},
mounted() {
// this.retrieve_blog_content()
if (this.$route.query?.request_id && this.$route.query?.document_id) {
this.retrieve_blog_content(this.$route.query.request_id, this.$route.query.document_id)
}
},
methods: {
// retrieve_blog_content() {
// ContentApi.retrieve_blog_content(this.content_id)
// .then((response) => {
// this.content_media_data = response.data
// var loadingTask = pdfjsLib.getDocument({
// data: Buffer.from(this.content_media_data, 'base64')
// })
// loadingTask.promise.then(function (pdf) {
// //
// // Fetch the first page
// //
// pdf.getPage(1).then(function (page) {
// //rendering
// })
// })
// })
// .catch((error) => {
// this.mnx_backendErrorHandler(error)
// })
// }
retrieve_blog_content(request_id, document_id) {
WorksapceApi.fetchAttachedFileAsMediaData(request_id, document_id).then(response => {
this.content_media_data = {url: response.data}
})
}
},
data() {
return {

View File

@ -6,6 +6,7 @@
v-for="(conversation, index) in conversations"
:key="index"
class="conversation-container"
:class="{selected: current_thread?.conversation?.information?.conversation_id === conversation.summary.last_message.conversation_id}"
@click="selectConversation(conversation)"
>
<img class="participant-portrait" alt="user portrait" src="@/assets/profile.png" />
@ -37,7 +38,7 @@
v-for="(item, index) in current_thread.conversation.messages"
:key="index"
class="message-item-container"
:class="item.sender_profile.me ? 'me' : ''"
:class="item.raw_data.sender_id == userIdentityNote ? 'me' : ''"
>
<div class="message-item-header-container">
<img
@ -84,7 +85,7 @@ export default {
},
watch: {
conversations: {
handler: function (val, oldVal) {
handler: function (val) {
if (val && val.length > 0) {
this.current_thread = val[0]
}
@ -94,6 +95,7 @@ export default {
},
data() {
return {
userIdentityNote: this.mnx_getUserIdentity(),
conversations: [],
current_thread: null,
writtenMessage: null
@ -125,7 +127,7 @@ export default {
})
},
getDateFromFulltimeString(fulltime) {
return DateUtils.FromJsonToDatetimeString(fulltime)
return DateUtils.FromJsonToHMDateString(fulltime)
}
}
}

View File

@ -1,267 +1,265 @@
<template>
<div class="provider-hub-container">
<div v-if="recommendedProviders" class="accordion" id="provider-accordion-container">
<div
v-for="(provider, index) in recommendedProviders"
:key="index"
class="accordion-item my-3"
>
<h2 class="accordion-header">
<button
class="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
:data-bs-target="'#collapse' + index"
aria-expanded="false"
:aria-controls="'collapse' + index"
<div class="provider-hub-container" id="provider-accordion-container">
<template v-if="recommendedProviders">
<div class="accordion accordion-list" v-for="(provider, index) in recommendedProviders" :key="index">
<div class="accordion-item my-3" >
<h2 class="accordion-header">
<button
class="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
:data-bs-target="'#collapse' + index"
aria-expanded="false"
:aria-controls="'collapse' + index"
>
<div class="provider-summary-containter dashed-container">
<div class="provider-portrait-containter">
<img
class="provider-portrait"
alt="user portrait"
src="@/assets/profile.png"
/>
</div>
<div class="provider-name-container">
<label class="provider-name-label" for="provider-name">Name</label>
<span class="provider-name-span" id="provider-name">
{{ provider.user_profile.first_name }} {{ provider.user_profile.last_name }}</span
>
</div>
<div class="provider-stay-on-freeleaps-container">
<label class="provider-stay-on-freeleaps-label" for="provider-stay-on-freeleaps"
>Stay on Freeleaps</label
>
<span class="provider-stay-on-freeleaps-span" id="provider-stay-on-freeleaps">
{{ provider.activeness_achievement.days_of_staying_on }} day(s)</span
>
</div>
<div class="provider-delivered-projects-container">
<label class="provider-delivered-projects-label" for="provider-delivered-projects"
>Delivered projects</label
>
<span class="provider-delivered-projects-span" id="provider-delivered-projects">
{{ provider.provider_achievement.delivered_projects }}
</span>
</div>
<div class="provider-responding-time-container">
<label class="provider-responding-time-label" for="provider-responding-time"
>Responding time</label
>
<span class="provider-responding-time-span" id="provider-responding-time">
{{ provider.provider_achievement.responding_time_in_minutes }} min(s)
</span>
</div>
<div class="provider-credit-score-container">
<label class="provider-credit-score-label" for="provider-credit-score"
>Credit score</label
>
<span class="provider-credit-score-span" id="provider-credit-score">
{{ provider.provider_achievement.credit }}</span
>
</div>
</div>
</button>
</h2>
<div
:id="'collapse' + index"
class="accordion-collapse collapse"
data-bs-parent="#provider-accordion-container"
>
<div class="provider-summary-containter">
<div class="provider-portrait-containter">
<img
class="provider-portrait"
alt="user portrait"
src="@/assets/images/default-user-portrait.png"
/>
<div class="accordion-body">
<div class="self-intro-container">
<label class="self-intro-content-label" for="self-intro-content">Self intro</label>
<div
class="self-intro-content-container"
id="self-intro-content"
v-html="provider.user_profile.self_intro.content_html"
></div>
</div>
<div class="provider-name-container">
<label class="provider-name-label" for="provider-name">Name</label>
<span class="provider-name-span" id="provider-name">
{{ provider.user_profile.first_name }} {{ provider.user_profile.last_name }}</span
>
</div>
<div class="provider-stay-on-freeleaps-container">
<label class="provider-stay-on-freeleaps-label" for="provider-stay-on-freeleaps"
>Stay on Freeleaps</label
>
<span class="provider-stay-on-freeleaps-span" id="provider-stay-on-freeleaps">
{{ provider.activeness_achievement.days_of_staying_on }} day(s)</span
>
</div>
<div class="provider-delivered-projects-container">
<label class="provider-delivered-projects-label" for="provider-delivered-projects"
>Delivered projects</label
>
<span class="provider-delivered-projects-span" id="provider-delivered-projects">
{{ provider.provider_achievement.delivered_projects }}
</span>
</div>
<div class="provider-responding-time-container">
<label class="provider-responding-time-label" for="provider-responding-time"
>Responding time</label
>
<span class="provider-responding-time-span" id="provider-responding-time">
{{ provider.provider_achievement.responding_time_in_minutes }} min(s)
</span>
</div>
<div class="provider-credit-score-container">
<label class="provider-credit-score-label" for="provider-credit-score"
>Credit score</label
>
<span class="provider-credit-score-span" id="provider-credit-score">
{{ provider.provider_achievement.credit }}</span
>
</div>
</div>
</button>
</h2>
<div
:id="'collapse' + index"
class="accordion-collapse collapse"
data-bs-parent="#provider-accordion-container"
>
<div class="accordion-body">
<div class="self-intro-container">
<label class="self-intro-content-label" for="self-intro-content">Self intro</label>
<div
class="self-intro-content-container"
id="self-intro-content"
v-html="provider.user_profile.self_intro.content_html"
></div>
</div>
<div class="statistics-container">
<label class="statistics-content-label" for="statistics-content">Profile</label>
<div class="statistics-content-container" id="statistics-content">
<div class="delivery-container">
<div class="delivery-delivered-project-container">
<label
class="delivery-delivered-projects-label"
for="delivery-delivered-projects"
>Delivered projects</label
>
<span class="delivery-delivered-projects-span" id="delivery-delivered-projects">
{{ provider.provider_achievement.delivered_projects }}
</span>
</div>
<div class="delivery-time-per-project-container">
<label class="delivery-time-per-project-label" for="delivery-time-per-project"
>Project delivering time</label
>
<span class="delivery-time-per-project-span" id="delivery-time-per-project">
{{ provider.provider_deliveries.delivering_time_per_project_in_day }} day(s)
</span>
</div>
<div class="delivery-top-programming-language-container">
<label
class="delivery-top-programming-language-label"
for="delivery-top-programming-language"
>Top programming languages</label
>
<div
class="delivery-top-programming-language-content-container"
id="delivery-top-programming-language"
>
<span
v-for="(lang, index) in provider.provider_deliveries
.top_programming_languages"
:key="index"
class="delivery-top-programming-language-span"
<div class="statistics-container">
<label class="self-intro-content-label" for="statistics-content">Profile</label>
<div class="statistics-content-container" id="statistics-content">
<div class="delivery-container">
<div class="dd-project-container">
<label
class="dd-project-label"
for="delivery-delivered-projects"
>Delivered projects</label
>
#{{ lang }}
<span class="dd-project-span" id="delivery-delivered-projects">
{{ provider.provider_achievement.delivered_projects }}
</span>
</div>
<div class="dd-project-container">
<label class="dd-project-label" for="delivery-time-per-project"
>Project delivering time</label
>
<span class="dd-project-span" id="delivery-time-per-project">
{{ provider.provider_deliveries.delivering_time_per_project_in_day }} day(s)
</span>
</div>
<div class="dd-project-container">
<label
class="dd-project-label"
for="delivery-top-programming-language"
>Top programming languages</label
>
<div
class="delivery-top-programming-language-content-container"
id="delivery-top-programming-language"
>
<span
v-for="(lang, index) in provider.provider_deliveries
.top_programming_languages"
:key="index"
class="dd-project-span"
>
#{{ lang }}
</span>
</div>
</div>
<div class="dd-project-container">
<label
class="dd-project-label"
for="delivery-weekly-produced-code"
>Weekly produced code</label
>
<span
class="dd-project-span"
id="delivery-weekly-produced-code"
>
{{ provider.provider_deliveries.lines_of_code_per_week }} line(s)
</span>
</div>
</div>
<div class="delivery-weekly-produced-code-container">
<label
class="delivery-weekly-produced-code-label"
for="delivery-weekly-produced-code"
>Weekly produced code</label
>
<span
class="delivery-weekly-produced-code-span"
id="delivery-weekly-produced-code"
>
{{ provider.provider_deliveries.lines_of_code_per_week }} line(s)
</span>
<div class="delivery-container">
<div class="dd-project-container">
<label class="dd-project-label" for="activity-ongoing-projects"
>Ongoing projects</label
>
<span class="dd-project-span" id="activity-ongoing-projects">
{{ provider.provider_activities.ongoing_projects }}
</span>
</div>
<div class="dd-project-container">
<label
class="dd-project-label"
for="activity-invitation-to-requests"
>Invitations to requests</label
>
<span
class="dd-project-span"
id="activity-invitation-to-requests"
>
{{ provider.provider_activities.invitations_to_open_requests }}
</span>
</div>
<div class="dd-project-container">
<label class="dd-project-label" for="activity-active-proposals"
>Active proposals</label
>
<span class="dd-project-span" id="activity-active-proposals">
{{ provider.provider_activities.active_proposals }}
</span>
</div>
<div class="dd-project-container">
<label class="dd-project-label" for="activity-hourly-rate"
>Expected hourly rate</label
>
<span class="dd-project-span" id="activity-hourly-rate">
{{ provider.provider_profile.expected_salary.hourly }}
{{ provider.provider_profile.expected_salary.currency }}
</span>
</div>
</div>
</div>
<div class="activity-container">
<div class="activity-ongoing-projects-container">
<label class="activity-ongoing-projects-label" for="activity-ongoing-projects"
>Ongoing projects</label
>
<span class="activity-ongoing-projects-span" id="activity-ongoing-projects">
{{ provider.provider_activities.ongoing_projects }}
</span>
</div>
<div class="activity-invitation-to-requests-container">
<label
class="activity-invitation-to-requests-label"
for="activity-invitation-to-requests"
>Invitations to requests</label
>
<span
class="activity-invitation-to-requests-span"
id="activity-invitation-to-requests"
>
{{ provider.provider_activities.invitations_to_open_requests }}
</span>
</div>
<div class="activity-active-proposals-container">
<label class="activity-active-proposals-label" for="activity-active-proposals"
>Active proposals</label
>
<span class="activity-active-proposals-span" id="activity-active-proposals">
{{ provider.provider_activities.active_proposals }}
</span>
</div>
<div class="delivery-hourly-rate-container">
<label class="delivery-hourly-rate-label" for="activity-hourly-rate"
>Expected hourly rate</label
>
<span class="delivery-hourly-rate-span" id="activity-hourly-rate">
{{ provider.provider_profile.expected_salary.hourly }}
{{ provider.provider_profile.expected_salary.currency }}
</span>
</div>
</div>
<div class="work-quality-container">
<div class="quality-code-issue-rate-container">
<label class="quality-code-issue-rate-label" for="quality-issue-rate"
>Code issue rate</label
>
<span class="quality-code-issue-rate-span" id="quality-issue-rate">
{{ provider.provider_work_quality.issues_per_thousand_lines_of_codes }}
</span>
</div>
<div class="quality-online-issue-rate-container">
<label class="quality-online-issue-rate-label" for="quality-online-issue-rate"
>Online issue rate</label
>
<span class="quality-online-issue-rate-span" id="quality-online-issue-rate">
{{ provider.provider_work_quality.issues_after_delivery_per_project }}
</span>
</div>
<div class="quality-issue-fixing-rate-container">
<label class="quality-issue-fixing-rate-label" for="quality-issue-fixing-rate"
>Issue fixing rate</label
>
<span class="quality-issue-fixing-rate-span" id="quality-issue-fixing-rate">
{{ provider.provider_work_quality.issue_fixing_rate_pencentage }}%
</span>
</div>
<div class="quality-issue-fixing-time-container">
<label class="quality-issue-fixing-time-label" for="quality-issue-fixing-time"
>Issue fixing time</label
>
<span class="quality-issue-fixing-time-span" id="quality-issue-fixing-time">
{{ provider.provider_work_quality.issue_fixing_time_minutes }} min(s)
</span>
<div class="delivery-container">
<div class="dd-project-container">
<label class="dd-project-label" for="quality-issue-rate"
>Code issue rate</label
>
<span class="dd-project-span" id="quality-issue-rate">
{{ provider.provider_work_quality.issues_per_thousand_lines_of_codes }}
</span>
</div>
<div class="dd-project-container">
<label class="dd-project-label" for="quality-online-issue-rate"
>Online issue rate</label
>
<span class="dd-project-span" id="quality-online-issue-rate">
{{ provider.provider_work_quality.issues_after_delivery_per_project }}
</span>
</div>
<div class="dd-project-container">
<label class="dd-project-label" for="quality-issue-fixing-rate"
>Issue fixing rate</label
>
<span class="dd-project-span" id="quality-issue-fixing-rate">
{{ provider.provider_work_quality.issue_fixing_rate_pencentage }}%
</span>
</div>
<div class="dd-project-container">
<label class="dd-project-label" for="quality-issue-fixing-time"
>Issue fixing time</label
>
<span class="dd-project-span" id="quality-issue-fixing-time">
{{ provider.provider_work_quality.issue_fixing_time_minutes }} min(s)
</span>
</div>
</div>
</div>
</div>
</div>
<div v-if="requests" class="accordion" id="accordion-action-panel">
<div class="accordion-item">
<h2 class="accordion-header">
<button
class="accordion-button"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapse-action-panel"
aria-expanded="false"
aria-controls="collapse-action-panel"
<div v-if="requests" class="accordion" id="accordion-action-panel">
<div class="accordion-item">
<h2 class="accordion-header">
<button
class="accordion-button"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapse-action-panel"
aria-expanded="false"
aria-controls="collapse-action-panel"
>
Action panel
</button>
</h2>
<div
id="collapse-action-panel"
class="accordion-collapse collapse"
data-bs-parent="#accordion-action-panel"
>
Action panel
</button>
</h2>
<div
id="collapse-action-panel"
class="accordion-collapse collapse"
data-bs-parent="#accordion-action-panel"
>
<div class="accordion-body">
<div class="invite-to-request-container">
<label class="invite-to-request-content-label" for="invite-to-request-content"
>Invite
<span class="invite-to-request-name-span"
>{{ provider.user_profile.first_name }}
{{ provider.user_profile.last_name }}</span
<div class="accordion-body">
<div class="invite-to-request-container">
<label class="invite-to-request-content-label" for="invite-to-request-content"
>Invite
<span class="invite-to-request-name-span"
>{{ provider.user_profile.first_name }}
{{ provider.user_profile.last_name }}</span
>
to my open requests</label
>
to my open requests</label
>
<div
class="invite-to-request-content-container"
id="invite-to-request-content"
>
<div class="form-check" v-for="(request, index) in requests" :key="index">
<input
class="form-check-input"
type="checkbox"
:value="request.id"
:id="'check' + request.id"
v-model="checkedRequests"
@change="
checkRequest($event, request.id, provider.user_profile.user_id)
"
/>
<label class="form-check-label" :for="'check' + request.id">{{
request.title
}}</label>
<div
class="invite-to-request-content-container"
id="invite-to-request-content"
>
<div class="form-check" v-for="(request, index) in requests" :key="index">
<input
class="form-check-input"
type="checkbox"
:value="request.id"
:id="'check' + request.id"
v-model="checkedRequests"
@change="
checkRequest($event, request.id, provider.user_profile.user_id)
"
/>
<label class="form-check-label" :for="'check' + request.id">{{
request.title
}}</label>
</div>
<span class="invite-to-request-note-text"
>*Once the request is selected, the provider will be invited to see the
request.</span
>
</div>
<span class="invite-to-request-note-text"
>*Once the request is selected, the provider will be invited to see the
request.</span
>
</div>
</div>
</div>
@ -271,7 +269,7 @@
</div>
</div>
</div>
</div>
</template>
</div>
</template>
@ -422,13 +420,16 @@ export default {
.self-intro-container {
@extend .flex-colum-container;
@extend .m-1;
@extend .border;
margin-bottom: 20px
// @extend .m-1;
// @extend .border;
}
.self-intro-content-label {
@extend .label-text-light;
@extend .w-100;
font-size: 14px;
font-weight: bold !important
}
.self-intro-content-container {
@ -439,43 +440,45 @@ export default {
.statistics-container {
@extend .flex-colum-container;
@extend .m-1;
@extend .border;
margin-bottom: 20px;
padding-top: 20px;
border-top: 1px solid #dee2e6;
}
.statistics-content-label {
@extend .label-text-light;
@extend .w-100;
}
// .statistics-content-label {
// @extend .label-text-light;
// @extend .w-100;
// }
.statistics-content-container {
@extend .flex-colum-container;
@extend .m-1;
padding: 0;
margin-top: 12px;
}
.delivery-container {
@extend .flex-row-container;
@extend .justify-content-around;
@extend .m-1;
@extend .border;
@extend .w-100;
background-color: rgba(32,90,239,.03);
margin-bottom: 12px;
padding: 12px 0;
}
.delivery-delivered-project-container {
.dd-project-container {
@extend .flex-colum-container;
@extend .w-20;
@extend .m-1;
}
.delivery-delivered-projects-label {
.dd-project-label {
@extend .label-text-light;
@extend .w-100;
}
.delivery-delivered-projects-span {
.dd-project-span {
@extend .text-start;
@extend .w-90;
@extend .m-1;
@extend .w-100;
font-weight: bold;
}
.delivery-time-per-project-container {
@ -526,7 +529,7 @@ export default {
.delivery-top-programming-language-content-container {
@extend .flex-row-container;
@extend .justify-content-start;
@extend .m-1;
padding: 0;
@extend .w-100;
}

View File

@ -6,13 +6,9 @@
class="request-invitations"
:id="group.name"
>
<div v-if="group.data" class="accordion" id="request-invitation-container">
<div
v-for="(request, index) in group.data"
:key="index"
:id="request.id"
class="accordion-item my-3"
>
<div v-if="group.data" id="request-invitation-container">
<div class="accordion accordion-list" v-for="(request, index) in group.data" :key="index" :id="request.id">
<div class="accordion-item my-3">
<h2 class="accordion-header">
<button
class="accordion-button collapsed"
@ -22,11 +18,9 @@
aria-expanded="false"
:aria-controls="'collapse' + index"
>
<div class="request-content-container">
<div class="dashed-container request-content-container">
<div class="request-content-issuer-container">
<label class="request-content-label" for="request-content-issuer-box"
>Issuer</label
>
<label class="request-content-label" for="request-content-issuer-box">Issuer</label>
<div class="request-content-box" id="request-content-issuer-box">
<span class="request-content-issuer-text">
{{ request.issuer_profile.first_name }}
@ -35,9 +29,7 @@
</div>
</div>
<div class="request-content-title-container">
<label class="request-content-label" for="request-content-title-box"
>Request</label
>
<label class="request-content-label" for="request-content-title-box">Request</label>
<div class="request-content-box" id="request-content-title-box">
<span class="request-content-title-text"> {{ request.title }}</span>
</div>
@ -82,9 +74,9 @@
</div>
</div>
<div class="issuer-profile-container">
<label class="issuer-profile-label" for="issuer-achievement-container"
<!-- <label class="issuer-profile-label" for="issuer-achievement-container"
>Issuer profile</label
>
> -->
<div class="issuer-achievement-container" id="issuer-achievement-container">
<div class="issuer-achievement-isssuer-container">
<label
@ -171,6 +163,8 @@
</div>
</div>
</div>
</div>
</div>
</template>
@ -208,15 +202,15 @@ export default {
return DateUtils.FromJsonToDateString(fulltime)
},
previewAttachedFile(request_id, document_id) {
WorksapceApi.fetchAttachedFileAsMediaData(request_id, document_id)
.then((response) => {
let media_data = response.data
console.log(media_data)
//TODO: navigate to the preview page
})
.catch((error) => {
this.mnx_backendErrorHandler(error)
})
this.mnx_navToPdfContentViewer(request_id, document_id)
// WorksapceApi.fetchAttachedFileAsMediaData(request_id, document_id)
// .then((response) => {
// let media_data = response.data
// console.log(media_data)
// })
// .catch((error) => {
// this.mnx_backendErrorHandler(error)
// })
},
downloadAttachedFile(request_id, document_id) {
WorksapceApi.fetchAttachedFileAsDownload(request_id, document_id)
@ -245,7 +239,6 @@ export default {
<style lang="scss" scoped>
.request-hub {
@extend .container;
@extend .my-1;
}
.request-invitations-header-text {
@ -254,8 +247,6 @@ export default {
.request-invitations {
@extend .container;
@extend .border;
@extend .my-1;
}
.request-content-container {
@ -313,13 +304,12 @@ export default {
}
.request-description-container {
@extend .container;
@extend .border;
min-height: 4vh;
margin-bottom: 20px
}
.make-proposal-button {
@extend .initiate-button;
@extend .btn;
@extend .btn-link;
@extend .float-end;
}
@ -328,8 +318,8 @@ export default {
}
.issuer-profile-container {
@extend .container;
@extend .border;
border-top: 1px solid #dee2e6;
padding-top: 15px
}
.issuer-profile-label {
@ -355,26 +345,31 @@ export default {
@extend .flex-colum-container-aligned-start;
@extend .w-10;
@extend .my-1;
@extend .text-start;
}
.issuer-achievement-issuer-text {
@extend .text-start;
font-weight: bold
}
.issuer-achievement-stay-container {
@extend .flex-colum-container-aligned-start;
@extend .w-10;
@extend .my-1;
@extend .text-start;
}
.issuer-achievement-stay-content-text {
@extend .text-start;
font-weight: bold
}
.issuer-achievement-paid-container {
@extend .flex-colum-container-aligned-start;
@extend .w-10;
@extend .my-1;
@extend .text-start;
}
.issuer-achievement-deposit-container {
@ -385,19 +380,23 @@ export default {
.issuer-achievement-deposit-content-text {
@extend .text-start;
font-weight: bold
}
.issuer-achievement-paid-content-text {
@extend .text-start;
font-weight: bold
}
.issuer-achievement-credit-container {
@extend .flex-colum-container-aligned-start;
@extend .w-10;
@extend .my-1;
@extend .text-start;
}
.issuer-achievement-credit-content-text {
@extend .text-start;
font-weight: bold
}
</style>

View File

@ -3,7 +3,7 @@
<!-- <div class="workspace-header"></div> -->
<div class="workspace-body">
<div
class="accordion"
class="accordion accordion-list"
v-for="(project, project_index) in projects"
:key="project_index"
:id="project.id"
@ -18,7 +18,7 @@
aria-expanded="false"
:aria-controls="'collapse-' + project_index"
>
<div class="workspace-item-bar">
<div class="workspace-item-bar dashed-container">
<div class="workspace-item-bar-left">
<div class="project-item-title-container">
<label class="project-item-label">Summary</label>
@ -927,38 +927,23 @@ export default {
width: 100%;
max-width: $body-width;
padding: 24px 0;
.accordion {
box-shadow: 0px 0px 24px 0px #d4d3e380;
border: none;
border-radius: 12px;
margin-bottom: 16px;
.accordion-item {
border: none;
.accordion-header {
border: none;
.accordion-button {
box-shadow: none;
padding: 12px;
}
}
}
}
}
.workspace-item-bar {
display: flex;
align-items: center;
margin: 0 24px 0 0;
border: 1px dashed #aebffd;
border-radius: 3px;
flex: 1;
// margin: 0 24px 0 0;
// border: 1px dashed #aebffd;
// border-radius: 3px;
// flex: 1;
}
.workspace-item-bar-left {
@extend .flex-row-container;
@extend .flex-grow-1;
padding: 0
// @extend .border;
@extend .me-3;
// @extend .me-3;
}
.workspace-item-bar-right {
@ -994,11 +979,11 @@ export default {
.project-item-text {
@extend .text-start;
@extend .mx-1;
margin-bottom: 0
}
.project-request-container {
@extend .container;
@extend .border;
// padding: 32px;
}
.float-action-container {
@ -1006,7 +991,9 @@ export default {
}
.request-action-withdraw {
@extend .initiate-button;
@extend .btn;
@extend .btn-link;
padding: 0
}
.project-request-content {
@ -1014,8 +1001,8 @@ export default {
}
.project-request-proposals-container {
@extend .container;
@extend .border;
// @extend .container;
// @extend .border;
}
.project-request-proposal-bar-container {
@ -1097,7 +1084,8 @@ export default {
}
.proposal-action-withdraw {
@extend .initiate-button;
@extend .btn;
@extend .btn-link;
}
.project-milestone-bar-container {

View File

@ -42,7 +42,7 @@
data-bs-parent="#existing-request-item-container"
>
<div class="accordion-body">
<button class="copy-existing-button" @click="copyRequest(existingRequest)">
<button class="btn btn-link" @click="copyRequest(existingRequest)">
Copy
</button>
<div class="existing-request-content-text" v-html="existingRequest.content"></div>
@ -84,7 +84,7 @@
aria-expanded="false"
:aria-controls="'collapse' + index"
>
{{ template.title }}
<span class="dashed-container">{{ template.title }}</span>
</button>
</h2>
<div
@ -94,7 +94,7 @@
data-bs-parent="#template-item-container"
>
<div class="accordion-body">
<button class="select-template-button" @click="selectTemplate(template)">
<button class="select-template-button btn btn-link" @click="selectTemplate(template)">
Apply
</button>
<div class="template-content-textarea" v-html="template.content"></div>
@ -125,22 +125,24 @@
</div>
<div class="flex-1" />
<button
class="action-button"
class="action-button btn btn-link"
type="button"
data-bs-toggle="offcanvas"
data-bs-target="#offcanvas-template"
aria-controls="offcanvas-template"
>
Templates...
<svg-icon icon="btn-templates" />
Templates
</button>
<button
class="action-button"
class="action-button btn btn-link"
type="button"
data-bs-toggle="offcanvas"
data-bs-target="#offcanvas-copy-existing"
aria-controls="offcanvas-copy-existing"
>
Copy...
<svg-icon icon="btn-history" />
Copy
</button>
</div>
<div class="description-container">
@ -160,8 +162,8 @@
<input type="file" id="file-upload" class="file-upload-input" @change="handleFileUpload" />
</div> -->
<div class="action-footer">
<button class="cancel-button" @click="back">Back</button>
<button class="submit-button" @click="submit">Submit</button>
<button class="cancel-button" @click="back">Cancel</button>
<button class="submit-button" @click="submit">(Re)Submit</button>
</div>
</div>
</template>
@ -186,7 +188,7 @@ export default {
}
},
components: { FreeleapsEditor, InputSelector, SvgIcon },
mounted() {
mounted () {
this.initProducts()
this.initiateFromInput()
this.fetchTemplates()
@ -347,17 +349,23 @@ export default {
}
.request-issue-container {
@extend .flex-colum-container;
box-shadow: 0px 0px 24px 0px rgba(212,211,227,0.5);
border-top-left-radius: 12px;
border-top-right-radius: 12px;
overflow: hidden
}
.action-bar {
@extend .flex-row-container;
@extend .justify-content-end;
border-bottom: 1px solid #E7E8EB;
padding-top: 6px;
padding-bottom: 6px;
}
.action-button {
@extend .initiate-button;
@extend .mx-3;
@extend .my-1;
margin-left: 16px;
// .action-btn-icon {color: $primary}
}
.title-container {
@ -373,6 +381,7 @@ export default {
.description-container {
@extend .w-100;
@extend .my-3;
flex: 1
}
.input-description {
@ -508,11 +517,13 @@ export default {
.cancel-button {
@extend .stop-button;
@extend .mx-3;
@extend .min-btn
}
.submit-button {
@extend .proceed-button;
@extend .mx-3;
@extend .min-btn
}
.offcanvas-parent {
@ -530,7 +541,7 @@ export default {
}
.select-template-button {
@extend .initiate-button;
// @extend .initiate-button;
@extend .float-end;
}

View File

@ -1,10 +1,13 @@
<template>
<div class="submission-result-container">
<p class="submission-result-text">The request has been submitted!</p>
<p class="submission-result-text">
You can find it in
<button class="workspace-button" @click="gotoWorkspace()">workspace</button>
</p>
<div class="submission-result-card">
<img src="@/assets/images/submited.png" alt="freeleaps">
<span>The request has been submitted!</span>
<span>
You can find it in
<button class="btn btn-link" @click="gotoWorkspace()">My work.</button>
</span>
</div>
</div>
</template>
@ -38,9 +41,33 @@ export default {
</script>
<style lang="scss" scoped>
.submission-result-container {
@extend .m-3;
@extend .p-3;
@extend .container;
@extend .flex-row-container;
align-items: center;
justify-content: center;
min-height: $body-height;
}
.submission-result-card {
width: fit-content;
min-width: 641px;
padding: 45px 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 26px;
line-height: 36px;
box-shadow: 0px 0px 24px 0px #D4D3E380;
img {
width: 86px;
margin-bottom: 20px
}
.btn-link {
text-decoration: underline;
padding: 0;
font-size: inherit
}
}
.submission-result-text {

View File

@ -111,7 +111,7 @@ const router = createRouter({
{
name: 'pdf-content-viewer',
path: '/pdf-content-viewer/:content_id',
path: '/pdf-content-viewer',
meta: { requiredRoles: [userRoleEnum.NONE] },
components: { default: PdfContentViewer, footer: FooterGuest, header: HeaderGuest },
props: true
@ -269,7 +269,7 @@ const router = createRouter({
{
name: 'request-submitted',
path: '/request-submitted/:requestId',
meta: { requiredRoles: [userRoleEnum.PERSONAL] },
meta: { requiredRoles: [userRoleEnum.PERSONAL], activePath: 'Post' },
components: { default: RequestSubmitted, footer: FooterGuest, header: HeaderUser },
props: true
},

View File

@ -38,6 +38,15 @@ class DateUtils {
}
}
static FromJsonToHMDateString(o) {
let date = DateUtils.FromJson(o)
if (date) {
return `${date.getUTCHours().toString().padStart(2, '0')}:${date.getUTCMinutes().toString().padStart(2, '0')}`
} else {
return null
}
}
static GetDeltaInDays(o) {
let date = o instanceof Date ? o : DateUtils.FromJson(o)
return Math.abs(Math.floor((date - Date.now()) / (1000 * 60 * 60 * 24)))