Skip to content

Commit 779ff07

Browse files
committed
Merge branch 'main' of github.com:efdevcon/monorepo
2 parents 9817b39 + fa5804f commit 779ff07

9 files changed

Lines changed: 462 additions & 55 deletions

File tree

devconnect/cms/pages/es/index.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ devconnect_themes:
152152
- Gaming
153153
- Arte
154154
faq:
155+
- question: Guía del viajero de Buenos Aires
156+
answer: >
157+
[Lea la guía del viajero
158+
aquí](https://docs.fileverse.io/0xa71a99940Bd85C173397c8aE3986960785c762B6/2#key=W0074ipXQf-mB7755hgizLDiXO3i8WGocceiwvjlQ6VmkxVs98G7xI-sBbrPbkAx)
155159
- question: ¿Puedo postularme para hablar?
156160
answer: >
157161
No tenemos aplicaciones para oradores para "Ethereum Day" en La Rural el

devconnect/cms/pages/index.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ devconnect_themes:
144144
- Gaming
145145
- Art
146146
faq:
147+
- question: Buenos Aires Traveler's Guide
148+
answer: >
149+
[Read the traveler's guide
150+
here](https://docs.fileverse.io/0xa71a99940Bd85C173397c8aE3986960785c762B6/2#key=W0074ipXQf-mB7755hgizLDiXO3i8WGocceiwvjlQ6VmkxVs98G7xI-sBbrPbkAx)
147151
- question: Can I apply to speak?
148152
answer: >
149153
We don't have speaker applications for "Ethereum Day" at La Rural on Nov

devconnect/cms/pages/pt/index.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ devconnect_themes:
149149
- Jogos
150150
- Arte
151151
faq:
152+
- question: Guia do viajante de Buenos Aires
153+
answer: >
154+
[Leia o guia do viajante
155+
aqui](https://docs.fileverse.io/0xa71a99940Bd85C173397c8aE3986960785c762B6/2#key=W0074ipXQf-mB7755hgizLDiXO3i8WGocceiwvjlQ6VmkxVs98G7xI-sBbrPbkAx)
152156
- question: Posso me inscrever para falar?
153157
answer: >
154158
Não temos inscrições para palestrantes para o "Dia do Ethereum" em La
74 KB
Loading

devconnect/src/common/components/ticket/index.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const colorMap = {
77
blue: { primary: '#74ACDF', secondary: '#417FB8' },
88
yellow: { primary: '#F6B40E', secondary: '#B2820A' },
99
pink: { primary: '#FF85A6', secondary: '#BF4465' },
10+
scholar: { primary: '#36364C', secondary: '#2A2A3A' },
1011
}
1112

1213
export const colorKeys = Object.keys(colorMap)
@@ -52,6 +53,59 @@ export const Ticket = ({
5253
console.log('width', width)
5354
console.log('height', height)
5455

56+
if (color === 'scholar') {
57+
// just show the image + name
58+
return (
59+
<div
60+
style={{
61+
display: 'flex',
62+
width: '100%',
63+
height: '100%',
64+
position: 'relative',
65+
top: 0,
66+
left: 0,
67+
backgroundColor: 'transparent',
68+
}}
69+
>
70+
<img
71+
style={{
72+
display: 'flex',
73+
position: 'absolute',
74+
top: 0,
75+
left: 0,
76+
}}
77+
src={`${SITE_URL}/argentina/${color}-${type}.png`}
78+
width={1200}
79+
height={630}
80+
/>
81+
<div
82+
style={{
83+
display: 'flex',
84+
flexDirection: 'column',
85+
width: '100%',
86+
height: '100%',
87+
position: 'absolute',
88+
top: 137,
89+
left: 135,
90+
overflow: 'hidden',
91+
}}
92+
>
93+
<div
94+
style={{
95+
display: 'flex',
96+
justifyContent: 'flex-start',
97+
fontSize: '56px',
98+
color: '#36364C',
99+
fontFamily: isLatinOnly(name) ? 'Roboto Condensed' : 'Noto Sans SC',
100+
}}
101+
>
102+
{name}
103+
</div>
104+
</div>
105+
</div>
106+
)
107+
}
108+
55109
return (
56110
<div
57111
style={{

devconnect/src/pages/api/notion/[...id].ts

Lines changed: 103 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,22 +72,61 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
7272
const { id } = req.query;
7373

7474
// Handle catch-all route - id is an array, we want the first element
75-
const pageId = Array.isArray(id) ? id[0] : id;
75+
const idParam = Array.isArray(id) ? id[0] : id;
7676

77-
if (!pageId || typeof pageId !== 'string') {
77+
if (!idParam || typeof idParam !== 'string') {
7878
return res.status(400).json({ error: 'Invalid page ID' });
7979
}
8080

81+
// Parse id-password format
82+
const idParts = idParam.split('-');
83+
if (idParts.length < 2) {
84+
return res.status(400).json({ error: 'Invalid page ID format. Expected id-password format' });
85+
}
86+
87+
const password = idParts.pop(); // Get the last part as password
88+
const pageId = idParts.join('-'); // Join remaining parts as page ID
89+
90+
if (!pageId || !password) {
91+
return res.status(400).json({ error: 'Invalid page ID or password format' });
92+
}
93+
8194
if (method === 'GET') {
82-
return handleGet(req, res, pageId);
95+
return handleGet(req, res, pageId, password);
8396
} else if (method === 'PATCH') {
84-
return handlePatch(req, res, pageId);
97+
return handlePatch(req, res, pageId, password);
8598
} else {
8699
return res.status(405).json({ error: 'Method not allowed' });
87100
}
88101
}
89102

90103

104+
// Helper function to validate password against page properties
105+
async function validatePassword(pageProperties: any, providedPassword: string): Promise<boolean> {
106+
const formPassword = pageProperties["Form password"];
107+
if (!formPassword) {
108+
return false; // No password set on the page
109+
}
110+
111+
// Extract password value based on property type
112+
let storedPassword = '';
113+
if (formPassword.type === 'rich_text') {
114+
storedPassword = formPassword.rich_text?.[0]?.plain_text || '';
115+
} else if (formPassword.type === 'title') {
116+
storedPassword = formPassword.title?.[0]?.plain_text || '';
117+
} else if (formPassword.type === 'select') {
118+
storedPassword = formPassword.select?.name || '';
119+
} else if (formPassword.type === 'formula') {
120+
// Handle formula type - convert number to string for comparison
121+
const formulaResult = formPassword.formula;
122+
if (formulaResult && formulaResult.type === 'number') {
123+
storedPassword = formulaResult.number?.toString() || '';
124+
}
125+
}
126+
127+
return storedPassword === providedPassword;
128+
}
129+
91130
// Helper function to fetch supporter data from rollup
92131
async function fetchSupporterFromRollup(pageProperties: any, notion: Client): Promise<string> {
93132
// Look for "Parent item" relation in the current page properties
@@ -121,7 +160,7 @@ async function fetchSupporterFromRollup(pageProperties: any, notion: Client): Pr
121160
}
122161

123162
// GET: Fetch page data for the given id
124-
async function handleGet(req: NextApiRequest, res: NextApiResponse, pageId: string) {
163+
async function handleGet(req: NextApiRequest, res: NextApiResponse, pageId: string, password: string) {
125164
const supporter = !req.query?.supporter ? false : true;
126165
const notion = new Client({ auth: process.env.NOTION_SECRET });
127166
try {
@@ -132,6 +171,12 @@ async function handleGet(req: NextApiRequest, res: NextApiResponse, pageId: stri
132171
return res.status(400).json({ error: 'Invalid page object' });
133172
}
134173

174+
// Validate password before proceeding
175+
const isPasswordValid = await validatePassword(page.properties, password);
176+
if (!isPasswordValid) {
177+
return res.status(401).json({ error: 'Invalid or missing password' });
178+
}
179+
135180

136181
// Get database schema to access field descriptions
137182
let databaseSchema: any = null;
@@ -303,8 +348,45 @@ async function handleGet(req: NextApiRequest, res: NextApiResponse, pageId: stri
303348
break;
304349
case 'relation':
305350
if (propertyAny.type === 'relation') {
306-
// return list of ids
307-
fieldValue = propertyAny.relation?.map((item: any) => `${item.id?.replaceAll('-', '')}`).join(',') || '';
351+
// For quests, we need to get the password for each quest if supporter is true
352+
if (supporter && propertyAny.relation && propertyAny.relation.length > 0) {
353+
// Get passwords for each quest
354+
const questIdsWithPasswords = await Promise.all(
355+
propertyAny.relation.map(async (item: any) => {
356+
try {
357+
const questPage = await notion.pages.retrieve({ page_id: item.id });
358+
const questProperties = (questPage as any).properties;
359+
360+
// Get the password from the quest page
361+
const questPassword = questProperties["Form password"];
362+
let password = '';
363+
364+
if (questPassword) {
365+
if (questPassword.type === 'formula' && questPassword.formula?.type === 'number') {
366+
password = questPassword.formula.number?.toString() || '';
367+
} else if (questPassword.type === 'rich_text') {
368+
password = questPassword.rich_text?.[0]?.plain_text || '';
369+
} else if (questPassword.type === 'title') {
370+
password = questPassword.title?.[0]?.plain_text || '';
371+
} else if (questPassword.type === 'select') {
372+
password = questPassword.select?.name || '';
373+
}
374+
}
375+
376+
// Return id-password format
377+
return password ? `${item.id?.replaceAll('-', '')}-${password}` : item.id?.replaceAll('-', '');
378+
} catch (error) {
379+
console.error('Failed to fetch quest password:', error);
380+
// Fallback to just the ID if password fetch fails
381+
return item.id?.replaceAll('-', '');
382+
}
383+
})
384+
);
385+
fieldValue = questIdsWithPasswords.join(',');
386+
} else {
387+
// return list of ids without passwords
388+
fieldValue = propertyAny.relation?.map((item: any) => `${item.id?.replaceAll('-', '')}`).join(',') || '';
389+
}
308390
fieldType = 'quests';
309391
}
310392
break;
@@ -428,15 +510,16 @@ async function handleGet(req: NextApiRequest, res: NextApiResponse, pageId: stri
428510
config: {
429511
isLocked,
430512
isOk
431-
}
513+
},
514+
accreditationInsuranceGuideUrl: process.env.ACCREDITATION_INSURANCE_GUIDE || ''
432515
});
433516
} catch (error) {
434517
return res.status(500).json({ error: 'Failed to fetch page data' });
435518
}
436519
}
437520

438521
// PATCH: Update page with submitted form data
439-
async function handlePatch(req: NextApiRequest, res: NextApiResponse, pageId: string) {
522+
async function handlePatch(req: NextApiRequest, res: NextApiResponse, pageId: string, password: string) {
440523
const notion = new Client({ auth: process.env.NOTION_SECRET });
441524
try {
442525
const formData = req.body;
@@ -449,6 +532,12 @@ async function handlePatch(req: NextApiRequest, res: NextApiResponse, pageId: st
449532
return res.status(400).json({ error: 'Invalid page object' });
450533
}
451534

535+
// Validate password before proceeding
536+
const isPasswordValid = await validatePassword(page.properties, password);
537+
if (!isPasswordValid) {
538+
return res.status(401).json({ error: 'Invalid or missing password' });
539+
}
540+
452541
// Check if any [config] field contains [lock] to prevent updates
453542
let isLocked = false;
454543
for (const [propertyName, property] of Object.entries(page.properties)) {
@@ -570,6 +659,11 @@ async function handlePatch(req: NextApiRequest, res: NextApiResponse, pageId: st
570659
// updates[propertyName] = { title: value ? [{ text: { content: value } }] : [] };
571660
// break;
572661
}
662+
663+
// Special handling for Insurance field - also update "Insurance link" field
664+
if (fieldName === 'Insurance' && value) {
665+
updates["Insurance link"] = { rich_text: value ? [{ text: { content: value } }] : [] }
666+
}
573667
}
574668

575669
await notion.pages.update({

0 commit comments

Comments
 (0)