Skip to content

Commit b3aa1c0

Browse files
Merge remote-tracking branch 'upstream/test' into test
2 parents 5dbc901 + 0e81966 commit b3aa1c0

File tree

10 files changed

+190
-62
lines changed

10 files changed

+190
-62
lines changed

src/app/layout.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { nunito_sans } from '#/lib/font';
2929
import { siteConfig } from '#/lib/site';
3030
import { getConfigs } from '#/services/common';
3131

32+
import { getAppConfig } from '@/utils/app';
3233
import { GoogleAnalytics } from '@/components/GoogleAnalytics';
3334
// import { StartOnOpenBuild } from '@/components/StartOnOpenBuild'
3435

@@ -106,7 +107,7 @@ export default async function RootLayout({ children }) {
106107
return (
107108
<html lang="en" data-theme="light" className={`${nunito_sans.className} light`} suppressHydrationWarning>
108109
<body>
109-
<ClientEntry config={configRes}>
110+
<ClientEntry config={{ static: getAppConfig(), dynamic: configRes }}>
110111
<DefaultLayout>{children}</DefaultLayout>
111112
</ClientEntry>
112113
<GoogleAnalytics />

src/app/learn/[type]/ChallengesStatus.js

+28-17
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,39 @@
1616

1717
'use client';
1818

19+
import clsx from 'clsx';
20+
21+
const statusMap = {
22+
0: {
23+
text: 'Closed',
24+
colorClassName: 'text-[#82ADD8]',
25+
borderClassName: 'border-[#82ADD8]',
26+
bgClassName: 'bg-[rgba(130,173,216,0.12)]',
27+
},
28+
1: {
29+
text: 'Soon',
30+
colorClassName: 'text-[#DCB259]',
31+
borderClassName: 'border-[#DCB259]',
32+
bgClassName: 'bg-[rgba(220,178,89,0.12)]',
33+
},
34+
2: {
35+
text: 'Ongoing',
36+
colorClassName: 'text-[#60CA98]',
37+
borderClassName: 'border-[#60CA98]',
38+
bgClassName: 'bg-[rgba(96,202,152,0.12)]',
39+
},
40+
};
41+
1942
export function ChallengesStatus({ data }) {
43+
const status = statusMap[data?.challenges_extra.course_challenges_extra_time_order];
44+
2045
return (
2146
<div className="mr-1">
22-
{data?.challenges_extra.course_challenges_extra_time_order === 2 && (
23-
<span
24-
className="bg-[rgba(96,202,152,0.12)] h-6 flex items-center rounded-[6px] px-2 text-xs border border-[#60CA98] text-[#60CA98]"
25-
>
26-
Ongoing
27-
</span>
28-
)}
29-
{data?.challenges_extra.course_challenges_extra_time_order === 1 && (
30-
<span
31-
className="bg-[rgba(220,178,89,0.12)] h-6 flex items-center rounded-[6px] px-2 text-xs border border-[#DCB259] text-[#DCB259]"
32-
>
33-
Soon
34-
</span>
35-
)}
36-
{data?.challenges_extra.course_challenges_extra_time_order === 0 && (
47+
{status && (
3748
<span
38-
className="bg-[rgba(130,173,216,0.12)] h-6 flex items-center rounded-[6px] px-2 text-xs border border-[#82ADD8] text-[#82ADD8]"
49+
className={clsx(status.bgClassName, 'h-6 flex items-center rounded-[6px] px-2 text-xs border', status.borderClassName, status.colorClassName)}
3950
>
40-
Closed
51+
{status.text}
4152
</span>
4253
)}
4354
</div>

src/app/learn/[type]/Container.js

+17-7
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@
1414
* limitations under the License.
1515
*/
1616

17+
import clsx from 'clsx';
18+
19+
import { PAGE_SIZE } from '@/constants/config';
20+
import { get } from '@/utils/request';
1721
import { NoData } from '@/components/NoData';
22+
1823
import { FilterToggle } from './FilterToggle';
1924
import { List } from './List';
2025
import { TopFilters } from './TopFilters';
21-
22-
import { get } from '@/utils/request';
23-
import { PAGE_SIZE } from '@/constants/config';
26+
import { ListSkeleton } from './ListSkeleton';
2427

2528
export async function Container({ type, searchParams }) {
2629
const page = Number(searchParams?.page) || 1;
@@ -34,29 +37,36 @@ export async function Container({ type, searchParams }) {
3437
const featured = searchParams?.recommend_type || '';
3538
const body_type = searchParams?.body_type || '';
3639
const lang = searchParams?.lang || '';
40+
3741
let URL;
42+
3843
if (type === 'courses' && lang) {
3944
URL = `v1/learn/course/opencourse?&skip=${(page - 1) * PAGE_SIZE}&take=${PAGE_SIZE}&labels=${labels}&order=${order}&search=${query}&recommend_type=${featured}&body_type=${body_type}&lang=${lang}`;
4045
} else if (type === 'challenges') {
4146
URL = `v1/learn/course/challenges?&skip=${(page - 1) * PAGE_SIZE}&take=${PAGE_SIZE}&labels=${labels}&order=${order}&search=${query}&status=${status}&feeds=${feeds}&c_type=${c_type}`;
4247
} else if (type === 'career_path') {
4348
URL = `/ts/v1/learn/general/course/grow_path?order=${order}`;
4449
}
45-
let data = { count: 0 };
50+
51+
let data;
4652

4753
if (URL) {
4854
const res = await get(URL, {isServer: true});
4955
data = res.data;
56+
} else {
57+
data = await Promise.resolve({ count: 0 });
5058
}
5159

60+
const courseLangNotSpecified = type === 'courses' && !lang;
61+
const EmptyPlaceholder = courseLangNotSpecified ? ListSkeleton : NoData;
62+
5263
return (
5364
<div className="flex-1 pb-14">
54-
<div className="flex flex-col-reverse justify-between md:flex-row md:items-center">
65+
<div className={clsx('flex flex-col-reverse justify-between md:flex-row md:items-center', { hidden: courseLangNotSpecified })}>
5566
<FilterToggle type={type} count={data.count} />
5667
<TopFilters type={type} />
5768
</div>
58-
59-
{data.count === 0 ? <NoData /> : <List type={type} data={data} />}
69+
{data.count === 0 ? <EmptyPlaceholder /> : <List type={type} data={data} />}
6070
</div>
6171
);
6272
}

src/app/learn/[type]/[id]/RightCard.js

+27-11
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const EnrollModal = dynamic(() => import('./EnrollModal'), {
6161
ssr: false,
6262
});
6363

64-
function ButtonGroup({ data, permission, loading, type, apply, enroll, switchLoading, payLoading, isPay, payment }) {
64+
function ButtonGroup({ data, permission, loading, type, apply, enroll, switchLoading, payLoading, isPay, payment, related }) {
6565
const router = useRouter();
6666
const { status } = useSession();
6767

@@ -76,14 +76,28 @@ function ButtonGroup({ data, permission, loading, type, apply, enroll, switchLoa
7676

7777
// console.log(data)
7878

79-
return <div>
80-
{data?.challenges_extra && data?.challenges_extra.course_challenges_extra_time_order === 0 ? (
81-
<div className="pb-6">
82-
<Button disabled fullWidth className={'flex-1'} >
83-
Closed
84-
</Button>
79+
if (data?.challenges_extra && data?.challenges_extra.course_challenges_extra_time_order === 0) {
80+
let buttonText = 'Closed';
81+
let handleClick;
82+
83+
if (related) {
84+
buttonText = 'Enroll in course';
85+
handleClick = () => router.push(`/learn/courses/${related.link}`);
86+
}
87+
88+
return (
89+
<div>
90+
<div className="pb-6">
91+
<Button disabled={!handleClick} fullWidth className="flex-1" onClick={handleClick}>
92+
{buttonText}
93+
</Button>
94+
</div>
8595
</div>
86-
) : (
96+
);
97+
}
98+
99+
return (
100+
<div>
87101
<div className="pb-6 flex gap-2">
88102
{data.base.course_series_quiz_id !== 0 && <Button
89103
onClick={() => window.open(`/quiz/${data.base.course_series_quiz_id}`)}
@@ -173,11 +187,11 @@ function ButtonGroup({ data, permission, loading, type, apply, enroll, switchLoa
173187
</Button>
174188
)}
175189
</div>
176-
)}
177-
</div>;
190+
</div>
191+
);
178192
}
179193

180-
export function LearnRightCard({ data, type, permission }) {
194+
export function LearnRightCard({ data, type, permission, related }) {
181195
// const { data: walletClient } = useWalletClient()
182196
const searchParams = useSearchParams();
183197
const mediaUrl = useMediaUrl();
@@ -372,6 +386,7 @@ export function LearnRightCard({ data, type, permission }) {
372386
payLoading={payLoading}
373387
isPay={isPay}
374388
payment={payment}
389+
related={related}
375390
/>
376391
</div>
377392
)}
@@ -568,6 +583,7 @@ export function LearnRightCard({ data, type, permission }) {
568583
payLoading={payLoading}
569584
isPay={isPay}
570585
payment={payment}
586+
related={related}
571587
/>
572588

573589
<hr className="border-gray-400" />

src/app/learn/[type]/[id]/page.js

+26-10
Original file line numberDiff line numberDiff line change
@@ -48,44 +48,60 @@ export async function generateMetadata({ params }) {
4848
},
4949
};
5050
}
51+
5152
export default async function LearnDetailsPage({ params, searchParams }) {
53+
const learnType = params.type;
54+
const learnId = params.id;
55+
5256
let datas;
53-
if (params.type === 'career_path') {
57+
58+
if (learnType === 'career_path') {
5459
datas = await Promise.all([
55-
get(`ts/v1/learn/general/course/grow_path/${params.id}`, {isServer: true}),
56-
get(`ts/v1/learn/general/course/grow_path/${params.id}/permission`, {isServer: true}),
60+
get(`ts/v1/learn/general/course/grow_path/${learnId}`, {isServer: true}),
61+
get(`ts/v1/learn/general/course/grow_path/${learnId}/permission`, {isServer: true}),
5762
]);
5863
} else {
5964
datas = await Promise.all([
60-
get(`v1/learn/course/${params.type === 'courses' ? 'opencourse' : 'challenges'}/${params.id}`, {isServer: true}),
61-
get(`ts/v1/learn/general/course/series/${params.id}/permission`, {isServer: true}),
65+
get(`v1/learn/course/${learnType === 'courses' ? 'opencourse' : 'challenges'}/${learnId}`, {isServer: true}),
66+
get(`ts/v1/learn/general/course/series/${learnId}/permission`, {isServer: true}),
6267
]);
6368
}
69+
6470
const [{ data }, { data: permission }] = [...datas];
6571

66-
return params.type !== 'career_path' ? (
72+
let related = null;
73+
74+
if (learnType === 'challenges' && data?.challenges_extra?.course_challenges_extra_time_order === 0) {
75+
const res = await get(`ts/v1/learn/general/course/challenges/${learnId}/link`, { isServer: true });
76+
77+
if (res.data.link.toString() !== learnId) {
78+
related = res.data;
79+
}
80+
}
81+
82+
return learnType !== 'career_path' ? (
6783
<>
6884
<PreviewAlert searchParams={searchParams} />
6985
<div className="mx-auto px-6 lg:flex max-w-[1400px] justify-center">
7086
<div className="flex flex-1 border-gray-400 pt-6 lg:border-r lg:pr-14">
7187
<div className="w-full">
7288
<div className="flex justify-between">
7389
<Back params={params} />
74-
<Share img={data?.base?.course_series_img} title={data?.base?.course_series_title} type={params.type} id={params.id} excerpt={data?.base?.course_series_summary}/>
90+
<Share img={data?.base?.course_series_img} title={data?.base?.course_series_title} type={learnType} id={learnId} excerpt={data?.base?.course_series_summary}/>
7591
</div>
7692
<Title data={data} />
77-
{params.type === 'challenges' && <ChallengesTags data={data} />}
93+
{learnType === 'challenges' && <ChallengesTags data={data} />}
7894
{data?.base?.course_series_summary && <Summary data={data} />}
7995
{data && <Author data={data} />}
8096
{data && <Tabs data={data} />}
8197
{data && <LearnInfo data={data} />}
82-
{data && data?.courses?.length > 0 && <Chapters type={params.type} data={data} id={data?.base?.course_series_id} />}
98+
{data && data?.courses?.length > 0 && <Chapters type={learnType} data={data} id={data?.base?.course_series_id} />}
8399
<div className="h-6" />
84100
{data && data?.speaker?.length > 0 && <Speaker data={data?.speaker} />}
85101
<div className="h-[72px]" />
86102
</div>
87103
</div>
88-
<LearnRightCard data={data} type={params.type} permission={permission} />
104+
<LearnRightCard data={data} type={learnType} permission={permission} related={related} />
89105
</div>
90106
</>
91107
) : <GrowPath params={params} data={data} permission={permission} />;

src/domain/profile/views/team-profile/TeamProfile.js

+12-8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import { useState } from 'react';
1818
import { useDebouncedCallback } from 'use-debounce';
1919

20+
import useAppConfig from '@/hooks/useAppConfig';
2021
import useMounted from '@/hooks/useMounted';
2122
import { isBlockDataValid } from '@/components/block-editor';
2223

@@ -81,9 +82,10 @@ function TeamProfileView({ data, activities }) {
8182
const [tabActive, setTabActive] = useState(1);
8283
const [blockContent, setBlockContent] = useState(null);
8384
const viewingSelf = useViewingSelf(data?.base.user_id);
85+
const devPlazaEnabled = useAppConfig('devPlaza.enabled');
8486

8587
useMounted(() => {
86-
fetchBlockContent(data?.base.user_id).then(res => {
88+
devPlazaEnabled && fetchBlockContent(data?.base.user_id).then(res => {
8789
if (res.success) {
8890
setBlockContent(res.data);
8991
}
@@ -105,13 +107,15 @@ function TeamProfileView({ data, activities }) {
105107

106108
return (
107109
<div className="md:pl-[410px] md:pb-14 md:pr-14">
108-
<CustomContent
109-
key={rerenderKey}
110-
className="mb-6"
111-
data={blockContent}
112-
onChange={handleBlockChange}
113-
editable={viewingSelf}
114-
/>
110+
{devPlazaEnabled && (
111+
<CustomContent
112+
key={rerenderKey}
113+
className="mb-6"
114+
data={blockContent}
115+
onChange={handleBlockChange}
116+
editable={viewingSelf}
117+
/>
118+
)}
115119
<TabBarWidget
116120
tabs={['Info', 'Activities']}
117121
tabClassName="h-14 md:h-9 md:w-[111px] md:first:hidden"

src/entry/index.js

+10-7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
'use client';
1818

19+
import { AppConfigContext } from '@/hooks/useAppConfig';
1920
import { ReduxProviders } from '#/state/provider';
2021

2122
import setInterceptorsForHttpClients from './aspects/http';
@@ -30,13 +31,15 @@ setInterceptorsForHttpClients();
3031
function ClientEntry({ config, children }) {
3132
return (
3233
<Providers>
33-
<RouterProgress>
34-
<Toast />
35-
<ReduxProviders datas={{ configs: config }}>
36-
{children}
37-
<RouteIntercept />
38-
</ReduxProviders>
39-
</RouterProgress>
34+
<AppConfigContext.Provider value={config.static}>
35+
<RouterProgress>
36+
<Toast />
37+
<ReduxProviders datas={{ configs: config.dynamic }}>
38+
{children}
39+
<RouteIntercept />
40+
</ReduxProviders>
41+
</RouterProgress>
42+
</AppConfigContext.Provider>
4043
</Providers>
4144
);
4245
}

src/shared/components/Share/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export function Share({ img, title, type, id, excerpt }) {
123123
</Modal>
124124
{invitationRecordsAvailable && (
125125
<Modal isOpen={recordOpen} closeModal={() => setRecordOpen(false)} container className="!w-[380px]">
126-
<ModalCloseIcon onClick={() => setOpen(false)} className="absolute top-[-32px] right-[-32px] cursor-pointer" />
126+
<ModalCloseIcon onClick={() => setRecordOpen(false)} className="absolute top-[-32px] right-[-32px] cursor-pointer" />
127127
<div className="py-4 px-6">
128128
<div className="flex items-center">
129129
<ArrowLeftIcon className="w-5 h-5 cursor-pointer relative z-10" onClick={() => {

src/shared/hooks/useAppConfig.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Copyright 2024 OpenBuild
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { createContext, useContext } from 'react';
18+
19+
import { _getAppConfig, getAppConfig } from '../utils/app';
20+
21+
const AppConfigContext = createContext(getAppConfig());
22+
23+
function useAppConfig(keyPath) {
24+
return _getAppConfig(useContext(AppConfigContext), keyPath);
25+
}
26+
27+
export { AppConfigContext };
28+
export default useAppConfig;

0 commit comments

Comments
 (0)