Skip to content

Implement api proxy #248

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

Merged
merged 4 commits into from
May 26, 2025
Merged
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
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# endpoint for API
NUXT_API_PARTY_ENDPOINTS_API_URL=http://localhost:5000/v2
NUXT_PUBLIC_COMMUNITY_API_URL=http://localhost:5000/v2
NUXT_PUBLIC_AUTH_BASE_URL=http://localhost:5000
NUXT_PUBLIC_TARGET_ENV=
168 changes: 84 additions & 84 deletions components/members/Filter.vue
Original file line number Diff line number Diff line change
@@ -76,104 +76,104 @@
</div>
</template>
<script setup>
import { reactive } from 'vue'
import { reactive } from 'vue'

const state = reactive({
isOpen: true,
name: '',
noneJosaMembers: false,
selected: '',
})
const state = reactive({
isOpen: true,
name: '',
noneJosaMembers: false,
selected: '',
})

const sortItems = [
{
value: 'member_since,DESC',
text: 'Member since (Newest)',
},
{
value: 'member_since,ASC',
text: 'Member since (Oldest)',
},
{
value: 'name,ASC',
text: 'By Name (A → Z)',
},
{
value: 'name,DESC',
text: 'By Name (Z → A)',
},
]
const sortItems = [
{
value: 'member_since,DESC',
text: 'Member since (Newest)',
},
{
value: 'member_since,ASC',
text: 'Member since (Oldest)',
},
{
value: 'name,ASC',
text: 'By Name (A → Z)',
},
{
value: 'name,DESC',
text: 'By Name (Z → A)',
},
]

const emit = defineEmits(['sortMembers', 'searchMember', 'filterMembers'])
const emit = defineEmits(['sortMembers', 'searchMember', 'filterMembers'])

const onCheck = (event) => {
state.noneJosaMembers = event
emit('filterMembers', event)
}
const onCheck = (event) => {
state.noneJosaMembers = event
emit('filterMembers', event)
}

const onSubmit = () => {
emit('searchMember', state.name)
}
const onSelect = () => {
emit('sortMembers', state.selected)
}
const scrollToView = () => {
document.getElementById('members-found-heading').scrollIntoView({
block: 'start',
behavior: 'smooth',
})
}
const onSubmit = () => {
emit('searchMember', state.name)
}
const onSelect = () => {
emit('sortMembers', state.selected)
}
const scrollToView = () => {
document.getElementById('members-found-heading').scrollIntoView({
block: 'start',
behavior: 'smooth',
})
}
</script>
<style lang="postcss" scoped>
.filter-container {
@apply bg-white p-4;
margin: 0 0;
margin-bottom: 2rem;
.filter-container {
@apply bg-white p-4;
margin: 0 0;
margin-bottom: 2rem;

@media screen and (min-width: 1032px) {
margin: 72px 0;
@media screen and (min-width: 1032px) {
margin: 72px 0;
}
}
.form-wrapper {
@apply h-fit;
}
}
.form-wrapper {
@apply h-fit;
}

.expand-button {
transition-duration: 0.5s;
transform: rotate(0deg);
@apply cursor-pointer outline-none lg:hidden;
}
.expand-button {
transition-duration: 0.5s;
transform: rotate(0deg);
@apply cursor-pointer outline-none lg:hidden;
}

.expand-button-collapsed {
transition-duration: 0.5s;
transform: rotate(-90deg);
@apply cursor-pointer ml-2 outline-none lg:hidden;
}
.expand-button-collapsed {
transition-duration: 0.5s;
transform: rotate(-90deg);
@apply cursor-pointer ml-2 outline-none lg:hidden;
}

.filter-collapsed {
@apply px-2 pb-0 pt-3;
margin-top: 0;
margin-left: auto;
margin-right: 0;
margin-bottom: 2rem;
background-color: #e3e7e9;
width: fit-content;
}
.filter-collapsed {
@apply px-2 pb-0 pt-3;
margin-top: 0;
margin-left: auto;
margin-right: 0;
margin-bottom: 2rem;
background-color: #e3e7e9;
width: fit-content;
}

.filter-icon {
width: 1rem;
margin-right: 0.6rem;
}
.filter-icon {
width: 1rem;
margin-right: 0.6rem;
}

.filter-button-container {
display: flex;
flex-direction: row;
}
.filter-button-container {
display: flex;
flex-direction: row;
}

.filter-head {
cursor: pointer;
@media screen and (min-width: 1024px) {
pointer-events: none;
.filter-head {
cursor: pointer;
@media screen and (min-width: 1024px) {
pointer-events: none;
}
}
}
</style>
144 changes: 53 additions & 91 deletions components/members/index.vue
Original file line number Diff line number Diff line change
@@ -1,108 +1,70 @@
<template>
<div v-if="state.loading" class="loader"></div>
<div v-else-if="!state.loading && state.members.length">
<div v-if="loading" class="loader"></div>
<div v-else-if="members?.items?.length">
<h2 class="text-2xl mono" id="members-found-heading">
{{ state.metaData.totalItems }} members found
{{ members.meta.totalItems }} members found
</h2>
<div v-for="(member, index) in state.members" :key="`item-${index}`">
<div v-for="(member, index) in members.items" :key="`item-${index}`">
<MembersCard :member="member" />
</div>
</div>
<div v-else-if="!state.loading && !state.members.length">
<div v-else-if="!members?.items?.length">
<p>No Members found</p>
</div>
<div v-else-if="!members">
<p>Error loading members or no members found.</p>
</div>
<PaginationBar
v-if="!state.loading && state.members.length"
:currentPage="state.page"
:totalPages="state.metaData.totalPages"
@switchPage="(newPage) => getMembers((state.page = newPage))"
v-if="!loading && members?.items?.length"
:currentPage="currentPage"
:totalPages="members.meta.totalPages"
@switchPage="(newPage) => (currentPage = newPage)"
/>
</template>
<script setup>
import { watch } from 'vue'

const config = useRuntimeConfig()
const state = reactive({
passedName: '',
loading: true,
noneJosaMembers: false,
orderBy: {
orderBy: '',
criteria: '',
},
members: [],
metaData: {},
page: 1,
})

const props = defineProps({
name: {
type: String,
default: '',
},
isChecked: {
type: Boolean,
default: false,
},
sortBy: {
type: String,
default: '',
},
})

const getMembers = async () => {
state.loading = true
let url = `${config.public.communityApiUrl}/member/page/${state.page}?`
if (state.passedName) {
url += `name=${state.passedName}&`
}
if (state.noneJosaMembers) {
url += `is_none_josa_member=${state.noneJosaMembers}&`
}

if (state.orderBy.orderBy) {
url += `order_by=${state.orderBy.orderBy}&order_criteria=${state.orderBy.criteria}&`
}
fetch(url)
.then((response) => response.json())
.then((data) => {
state.members = Object.create(data.items)
state.metaData = Object.create(data.meta)
})
.catch((error) => {
console.log(error)
})
.finally(() => {
state.loading = false
})
}
import qs from 'qs'
const currentPage = ref(1)

await getMembers()
const props = defineProps({
name: {
type: String,
default: '',
},
isChecked: {
type: Boolean,
default: true,
},
sortBy: {
type: String,
default: '',
},
})

// search member by name
watch(
() => (state.passedName = props.name),
async (newValue) => {
state.passedName = newValue
await getMembers()
}
)
const apiQueryParameters = computed(() => {
const params = {}
if (props.name && props.name.length > 0) {
params.name = props.name
}
params.is_none_josa_member = props.isChecked

// filter none members
watch(
() => (state.noneJosaMembers = props.isChecked),
async () => {
await getMembers()
}
)
if (props.sortBy) {
const [criteria, orderBy] = props.sortBy.split(',')
if (orderBy && criteria) {
params.order_by = orderBy
params.order_criteria = criteria
}
}
return params
})

// Order members
watch(
() => (state.orderBy = props.sortBy),
async (sortKey) => {
const sortArr = sortKey.split(',')
state.orderBy = { orderBy: sortArr[1], criteria: sortArr[0] }
await getMembers()
}
)
const { data: members, pending: loading } = await useLazyAsyncData(
() =>
$api(
`member/page/${currentPage.value}?${qs.stringify(apiQueryParameters.value)}`,
),
{
watch: [apiQueryParameters, currentPage],
default: () => ({ items: [], meta: { totalItems: 0, totalPages: 0 } }),
},
)
</script>
9 changes: 9 additions & 0 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@ export default defineNuxtConfig({
'@nuxtjs/robots',
'@nuxtjs/sitemap',
'@sidebase/nuxt-auth',
'nuxt-api-party',
],

css: [
@@ -58,6 +59,14 @@ export default defineNuxtConfig({
},
],

apiParty: {
endpoints: {
api: {
url: '',
},
},
},

// REF: https://v3.nuxtjs.org/guide/going-further/runtime-config/
runtimeConfig: {
// Keys within public, will be also exposed to the client-side
235 changes: 213 additions & 22 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -18,12 +18,14 @@
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-vue": "^10.1.0",
"nuxt": "^3.13.1",
"nuxt-api-party": "^2.2.2",
"prettier": "^3.3.3"
},
"dependencies": {
"@nuxt/vite-builder": "^3.13.1",
"@nuxtjs/robots": "^5.2.10",
"@vuepic/vue-datepicker": "^9.0.3",
"qs": "^6.14.0",
"vue-matomo": "^4.2.0"
}
}
95 changes: 49 additions & 46 deletions pages/register/index.vue
Original file line number Diff line number Diff line change
@@ -41,54 +41,57 @@
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
// definePageMeta({
// middleware: ['register'],
// })
const config = useRuntimeConfig()
const step = ref(1)
let formData = reactive({})
if (!config.public.targetEnv === 'development') {
navigateTo('/')
}
const nextStep = (data) => {
formData = { ...formData, ...data }
step.value++
}
import { ref, reactive } from 'vue'
const config = useRuntimeConfig()
const step = ref(1)
let formData = reactive({})
if (!config.public.targetEnv === 'development') {
navigateTo('/')
}
const nextStep = (data) => {
formData = { ...formData, ...data }
step.value++
}
const submitForm = (data) => {
formData = { ...formData, ...data }
fetch(`${config.public.communityApiUrl}/submission/`, {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
body: JSON.stringify(formData),
})
.then((res) => {
console.log(res)
step.value++
})
.catch((error) => {
console.log(error)
})
}
const submitForm = async (data) => {
formData = { ...formData, ...data }
await $api(
`submission/`,
{
method: 'POST',
body: formData,
},
{
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
},
)
.then((res) => {
console.log(res)
step.value++
})
.catch((error) => {
console.log(error)
})
}
</script>
<style lang="postcss" scoped>
.main {
@apply container;
@apply my-14;
@apply flex flex-col lg:flex-row;
}
.main,
.form-container {
@apply py-10;
}
.main {
@apply container;
@apply my-14;
@apply flex flex-col lg:flex-row;
}
.main,
.form-container {
@apply py-10;
}
.form-container {
@apply bg-white;
@apply px-10;
flex-grow: 1;
}
.form-container {
@apply bg-white;
@apply px-10;
flex-grow: 1;
}
</style>