we hebben login nu goed, dan sheet met : G20 — Dagelijks Geluksbesef
Coaching by Gabeler.eu | Coach Stijn Gabeler
Gabeler.eu | Coach Stijn
G20 dagboek succesvol opgeslagen in alle kolommen!
G20 Dagboek invullen
Terug
Selecteer Datum
Algemeen Inzicht van de dag
Mooiste Moment
Stap voor Morgen
De 20 G's van Bewustwording
Vul hieronder jouw persoonlijke reflecties in op de 20 G-thema's.
😊
1. Geluksbesef (GELUKSBESEF)
Waar werd je vandaag gelukkig, blij of dankbaar van?
❤️
2. Gevoelens (GEVOELENS)
Welke gevoelens waren vandaag aanwezig?
💭
3. Gedroomd (GEDROOMD)
Waar heb je vandaag over gedroomd of gefantaseerd?
🫶
4. Gemis (GEMIS)
Wat of wie heb je vandaag gemist?
🌱
5. Gewenst (GEWENST)
Wat had je vandaag graag anders, beter of mooier gewild?
✨
6. Gehoopt (GEHOOPT)
Waar hoopte je vandaag op?
🧠
7. Gedachten (GEDACHTEN)
Welke gedachte bleef vandaag bij je hangen?
🚶
8. Gedrag (GEDRAG)
Hoe heb je vandaag gehandeld?
💬
9. Gezegd (GEZEGD)
Wat heb je vandaag gezegd dat belangrijk voor je was?
✅
10. Gedaan (GEDAAN)
Wat heb je vandaag gedaan waar je bewust bij wilt stilstaan?
✍️
11. Geschreven (GESCHREVEN)
Wat heb je vandaag geschreven?
📖
12. Gelezen (GELEZEN)
Wat heb je vandaag gelezen dat je raakte of iets leerde?
🎓
13. Geleerd (GELEERD)
Wat heb je vandaag geleerd?
🧭
14. Geweten (GEWETEN)
Wat wist je vandaag diep vanbinnen al?
👂
15. Gehoord (GEHOORD)
Wat heb je vandaag gehoord dat je bijblijft?
👀
16. Gezien (GEZIEN)
Wat heb je vandaag bewust gezien?
😋
17. Geproefd (GEPROEFD)
Wat heb je vandaag geproefd en bewust beleefd?
🌸
18. Geroken (GEROKEN)
Welke geur heb je vandaag bewust opgemerkt?
🔎
19. Gedacht over (GEDACHT_OVER)
Waar heb je vandaag langer of dieper over nagedacht?
🔢
20. Geluksgetal (GELUKSGETAL)
Welk getal heeft vandaag betekenis voor jou?
Dagboek Opslaan Terug naar Menu
© 2026 Coaching by Gabeler.eu — Stijn Gabeler | Bezoek Gabeler.eu , zie : https://docs.google.com/spreadsheets/d/1cl2uFtN05D6Yw78R9QtpiVMBjvXGINmMdI01A3Fhons/edit?gid=1304956047#gid=1304956047 en zorg dat ook alle kolommen worden ingevuld zoals in de laatste rij is gelukt, RecordIDUserIDDatumG1_GeluksbesefG2_GevoelensG3_GedroomdG4_GemisG5_GewenstG6_GehooptG7_GedachtenG8_GedragG9_GezegdG10_GedaanG11_GeschrevenG12_GelezenG13_GeleerdG14_GewetenG15_GehoordG16_GezienG17_GeproefdG18_GerokenG19_GedachtOverG20_GeluksgetalInzichtMooisteMomentMorgenStapCreatedAtUpdatedAtU_17897748984602026-09-18U_17897748984602026-09-19Gabeler.eu GODOGOOD.eu groeit door geluksbesef G20 en ontwikkelt meer coachingkansen.2h47 , dit is het tijdstip dat ik dit schrijf, 247 is ook een mooie betekenis, TwentyFourSeven. Voor Altijd.dit verder ontwikkelen.U_17897748984602026-09-19doorgaande Next level versie247 remember now 315, today is just today, tomorrow will be great too
REC_1789783365905U_17897832347902026-09-19aaaaaaabbbbbbb123221313131239/19/2026 4:02:469/19/2026 4:15:53REC_1789784839986U_17897847266562026-09-19xxxav9/19/2026 4:27:209/19/2026 4:51:18REC_1789786318869U_17897847266562026-09-20G1_GeluksbesefG2_GevoelensG3_GedroomdG4_GemisG5_GewenstG6_GehooptG7_GedachtenG8_GedragG9_GezegdG10_GedaanG11_GeschrevenG12_GelezenG13_GeleerdG14_GewetenG15_GehoordG16_GezienG17_GeproefdG18_GerokenG19_GedachtOverG20_GeluksgetalInzichtMooisteMomentMorgenStap9/19/2026 4:51:599/19/2026 4:51:59 maak een Code.gs die klopt met : function doGet(e) {
return HtmlService.createHtmlOutputFromFile('Index')
.setTitle('G20 — Dagelijks Geluksbesef | NextLevel Coaching by Gabeler.eu')
.addMetaTag('viewport', 'width=device-width, initial-scale=1.0');
}
function getActiveSpreadsheet_() {
try {
var ss = SpreadsheetApp.getActiveSpreadsheet();
if (ss) return ss;
} catch(e) {}
return SpreadsheetApp.openById('1cl2uFtN05D6Yw78R9QtpiVMBjvXGINmMdI01A3Fhons');
}
function setupG20() {
try {
var ss = getActiveSpreadsheet_();
ensureSheetsExist_(ss);
return { success: true, message: 'Alle G20 tabbladen en structuren zijn succesvol gecontroleerd.' };
} catch(e) {
return { success: false, message: 'Fout bij setupG20: ' + e.toString() };
}
}
function testG20() {
return {
success: true,
app: 'G20 — Dagelijks Geluksbesef (NextLevel)',
coach: 'Coach Stijn Gabeler',
message: 'NextLevel Code.gs werkt correct.'
};
}
function ensureSheetsExist_(ss) {
var gKeys = [
'G1_Geluksbesef', 'G2_Gevoelens', 'G3_Gedroomd', 'G4_Gemis', 'G5_Gewenst',
'G6_Gehoopt', 'G7_Gedachten', 'G8_Gedrag', 'G9_Gezegd', 'G10_Gedaan',
'G11_Geschreven', 'G12_Gelezen', 'G13_Geleerd', 'G14_Geweten', 'G15_Gehoord',
'G16_Gezien', 'G17_Geproefd', 'G18_Geroken', 'G19_GedachtOver', 'G20_Geluksgetal'
];
var dailyHeaders = ['RecordID', 'UserID', 'Datum'].concat(gKeys).concat(['Inzicht', 'MooisteMoment', 'MorgenStap', 'CreatedAt', 'UpdatedAt']);
var required = {
'G20_Users': ['UserID', 'Naam', 'Email', 'PasswordHash', 'Status', 'CreatedAt', 'LastLogin', 'ProfileQuote', 'FavoriteTheme', 'Phone', 'Bio'],
'G20_Daily': dailyHeaders,
'G20_Questions': ['Nummer', 'Code', 'Titel', 'Emoji', 'Vraag', 'Reflectie'],
'G20_Cards': ['CardID', 'G20', 'Titel', 'Vraag', 'Reflectie', 'Emoji', 'Active'],
'G20_Codes': ['CodeID', 'Email', 'Code', 'Purpose', 'CreatedAt', 'ExpiresAt', 'Used'],
'G20_Favorites': ['FavoriteID', 'UserID', 'Type', 'ReferenceID', 'Title', 'G20', 'Note', 'CreatedAt'],
'G20_Sharing': ['ShareID', 'OwnerEmail', 'ViewerEmail', 'PeriodType', 'StartDate', 'EndDate', 'CreatedAt'] // Nieuw tabblad voor delen
};
for (var name in required) {
var sheet = ss.getSheetByName(name);
if (!sheet) {
sheet = ss.insertSheet(name);
sheet.appendRow(required[name]);
if (name === 'G20_Questions') {
seedQuestions_(sheet);
}
} else if (name === 'G20_Daily') {
var currentHeaders = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var missing = dailyHeaders.filter(function(h) { return currentHeaders.indexOf(h) === -1; });
if (missing.length > 0) {
sheet.getRange(1, currentHeaders.length + 1, 1, missing.length).setValues([missing]);
}
} else if (name === 'G20_Questions') {
if (sheet.getLastRow() <= 1) {
seedQuestions_(sheet);
}
}
}
}
function seedQuestions_(sheet) {
if (sheet.getLastRow() > 1) {
sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn()).clearContent();
}
var defaultQuestions = [
[1, "G1_Geluksbesef", "Geluksbesef", "😊", "Waar werd je vandaag gelukkig, blij of dankbaar van?", "Geluk wordt zichtbaar wanneer je er bewust aandacht aan geeft."],
[2, "G2_Gevoelens", "Gevoelens", "❤️", "Welke gevoelens waren vandaag aanwezig?", "Gevoelens mogen eerst worden opgemerkt."],
[3, "G3_Gedroomd", "Gedroomd", "💭", "Waar heb je vandaag over gedroomd of gefantaseerd?", "Een droom vertelt wat belangrijk is."],
[4, "G4_Gemis", "Gemis", "🫶", "Wat of wie heb je vandaag gemist?", "Gemis maakt waardes zichtbaar."],
[5, "G5_Gewenst", "Gewenst", "🌱", "Wat had je vandaag graag anders, beter of mooier gewild?", "Een wens wijst een richting aan."],
[6, "G6_Gehoopt", "Gehoopt", "✨", "Waar hoopte je vandaag op?", "Hoop geeft ruimte voor de toekomst."],
[7, "G7_Gedachten", "Gedachten", "🧠", "Welke gedachte bleef vandaag bij je hangen?", "Gedachten zijn feiten noch bevelen."],
[8, "G8_Gedrag", "Gedrag", "🚶", "Hoe heb je vandaag gehandeld?", "Je gedrag volgt je intentie."],
[9, "G9_Gezegd", "Gezegd", "💬", "Wat heb je vandaag gezegd dat belangrijk voor je was?", "Woorden hebben impact."],
[10, "G10_Gedaan", "Gedaan", "✅", "Wat heb je vandaag gedaan waar je bewust bij wilt stilstaan?", "Daden spreken dieper dan intenties."],
[11, "G11_Geschreven", "Geschreven", "✍️", "Wat heb je vandaag geschreven?", "Schrijven structureert je binnenwereld."],
[12, "G12_Gelezen", "Gelezen", "📖", "Wat heb je vandaag gelezen dat je raakte of iets leerde?", "Inspiratie voedt je blik."],
[13, "G13_Geleerd", "Geleerd", "🎓", "Wat heb je vandaag geleerd?", "Groei ontstaat door reflectie."],
[14, "G14_Geweten", "Geweten", "🧭", "Wat wist je vandaag diep vanbinnen al?", "Je geweten is je zuivere kompas."],
[15, "G15_Gehoord", "Gehoord", "👂", "Wat heb je vandaag gehoord dat je bijblijft?", "Luisteren brengt verbinding."],
[16, "G16_Gezien", "Gezien", "👀", "Wat heb je vandaag bewust gezien?", "Aandacht maakt details zichtbaar."],
[17, "G17_Geproefd", "Geproefd", "😋", "Wat heb je vandaag geproefd en bewust beleefd?", "Ervaringen kleuren je dag."],
[18, "G18_Geroken", "Geroken", "🌸", "Welke geur heb je vandaag bewust opgemerkt?", "Geur brengt je bij het moment."],
[19, "G19_GedachtOver", "Gedacht over", "🔎", "Waar heb je vandaag langer of dieper over nagedacht?", "Overpeinzing brengt helderheid."],
[20, "G20_Geluksgetal", "Geluksgetal", "🔢", "Welk getal heeft vandaag betekenis voor jou?", "Cijfers geven structuur aan beleving."]
];
defaultQuestions.forEach(function(row) {
sheet.appendRow(row);
});
}
// --- AUTH & USER MANAGEMENT ---
function g20Login(email, password) {
try {
var ss = getActiveSpreadsheet_();
var sheet = ss.getSheetByName('G20_Users');
if (!sheet) return { success: false, message: 'Systeem niet geïnitialiseerd.' };
var data = sheet.getDataRange().getValues();
var inputHash = hashCode_(password);
var oldHash = oldHashCode_(password);
for (var i = 1; i < data.length; i++) {
var rowEmail = data[i][2] ? data[i][2].toString().trim().toLowerCase() : '';
if (rowEmail === email.trim().toLowerCase()) {
var storedHash = data[i][3] ? data[i][3].toString().trim() : '';
if (storedHash === inputHash || storedHash === oldHash || storedHash === password) {
if (storedHash !== inputHash) {
sheet.getRange(i + 1, 4).setValue(inputHash);
}
sheet.getRange(i + 1, 7).setValue(new Date());
var userObj = {
userId: data[i][0],
name: data[i][1],
email: data[i][2],
status: data[i][4] || 'ACTIVE',
quote: data[i][7] || '',
favoriteTheme: data[i][8] || ''
};
return { success: true, user: userObj, message: 'Succesvol ingelogd.' };
} else {
return { success: false, message: 'Onjuist wachtwoord.' };
}
}
}
return { success: false, message: 'Geen account gevonden met dit e-mailadres.' };
} catch(e) {
return { success: false, message: 'Fout bij inloggen: ' + e.toString() };
}
}
function g20RequestRegistrationCode(name, email, password) {
try {
var ss = getActiveSpreadsheet_();
var sheet = ss.getSheetByName('G20_Users');
if (!sheet) {
setupG20();
sheet = ss.getSheetByName('G20_Users');
}
var data = sheet.getDataRange().getValues();
for (var i = 1; i < data.length; i++) {
if (data[i][2] && data[i][2].toString().trim().toLowerCase() === email.trim().toLowerCase()) {
return { success: false, message: 'Dit e-mailadres is al geregistreerd.' };
}
}
var userId = 'U_' + new Date().getTime();
var hash = hashCode_(password);
var mailCode = Math.floor(100000 + Math.random() * 900000).toString();
sheet.appendRow([userId, name, email.trim(), hash, 'PENDING_VERIFICATION', new Date(), '', 'Jij bepaalt je eigen koers.', 'Geluksbesef', '', '']);
var codesSheet = ss.getSheetByName('G20_Codes');
if (codesSheet) {
var expiresAt = new Date(new Date().getTime() + 15 * 60000);
codesSheet.appendRow(['CODE_' + new Date().getTime(), email.trim(), mailCode, 'register', new Date(), expiresAt, 'FALSE']);
}
try {
MailApp.sendEmail({
to: email,
subject: 'Jouw verificatiecode — G20 NextLevel',
body: 'Beste ' + name + ',\n\nWelkom bij G20 NextLevel.\nJe 6-cijferige verificatiecode is: ' + mailCode + '\n\nCoach Stijn Gabeler'
});
} catch(e) {}
return { success: true, message: 'Verificatiecode is succesvol verzonden naar je e-mailadres.' };
} catch(e) {
return { success: false, message: 'Fout bij registratie: ' + e.toString() };
}
}
function g20VerifyCode(email, code) {
try {
var ss = getActiveSpreadsheet_();
var codesSheet = ss.getSheetByName('G20_Codes');
var usersSheet = ss.getSheetByName('G20_Users');
var verified = false;
if (codesSheet) {
var codesData = codesSheet.getDataRange().getValues();
for (var i = 1; i < codesData.length; i++) {
if (codesData[i][1].toString().trim().toLowerCase() === email.trim().toLowerCase() &&
codesData[i][2].toString().trim() === code.toString().trim() &&
codesData[i][3] === 'register' &&
codesData[i][6].toString() !== 'TRUE') {
codesSheet.getRange(i + 1, 7).setValue('TRUE');
verified = true;
break;
}
}
}
if (!verified && code.length === 6) {
verified = true;
}
if (verified) {
var usersData = usersSheet.getDataRange().getValues();
for (var j = 1; j < usersData.length; j++) {
if (usersData[j][2] && usersData[j][2].toString().trim().toLowerCase() === email.trim().toLowerCase()) {
usersSheet.getRange(j + 1, 5).setValue('ACTIVE');
return { success: true, message: 'Account succesvol geactiveerd! Je kunt nu inloggen.' };
}
}
}
return { success: false, message: 'Onjuiste of verlopen verificatiecode.' };
} catch(e) {
return { success: false, message: 'Fout bij verifiëren: ' + e.toString() };
}
}
function g20RequestPasswordReset(email) {
try {
var ss = getActiveSpreadsheet_();
var usersSheet = ss.getSheetByName('G20_Users');
var codesSheet = ss.getSheetByName('G20_Codes');
var data = usersSheet.getDataRange().getValues();
var found = false;
for (var i = 1; i < data.length; i++) {
if (data[i][2] && data[i][2].toString().trim().toLowerCase() === email.trim().toLowerCase()) {
found = true;
break;
}
}
if (!found) {
return { success: false, message: 'Dit e-mailadres is niet bekend.' };
}
var resetCode = Math.floor(100000 + Math.random() * 900000).toString();
var expiresAt = new Date(new Date().getTime() + 15 * 60000);
if (codesSheet) {
codesSheet.appendRow(['RESET_' + new Date().getTime(), email.trim(), resetCode, 'reset', new Date(), expiresAt, 'FALSE']);
}
try {
MailApp.sendEmail({
to: email,
subject: 'Wachtwoord herstellen — G20 NextLevel',
body: 'Je herstelcode is: ' + resetCode + '\n\nGebruik deze code in je app om je nieuwe wachtwoord in te stellen.'
});
} catch(e) {}
return { success: true, message: 'Herstelcode is succesvol verzonden naar je e-mailadres.' };
} catch(e) {
return { success: false, message: 'Fout bij herstel aanvragen: ' + e.toString() };
}
}
function g20ResetPassword(email, code, newPassword) {
try {
var ss = getActiveSpreadsheet_();
var codesSheet = ss.getSheetByName('G20_Codes');
var usersSheet = ss.getSheetByName('G20_Users');
var codeValid = false;
if (codesSheet) {
var cData = codesSheet.getDataRange().getValues();
for (var i = 1; i < cData.length; i++) {
if (cData[i][1].toString().trim().toLowerCase() === email.trim().toLowerCase() &&
cData[i][2].toString().trim() === code.toString().trim() &&
cData[i][3] === 'reset' &&
cData[i][6].toString() !== 'TRUE') {
codesSheet.getRange(i + 1, 7).setValue('TRUE');
codeValid = true;
break;
}
}
}
if (!codeValid && code.length === 6) {
codeValid = true;
}
if (!codeValid) {
return { success: false, message: 'Onjuiste of verlopen herstelcode.' };
}
var uData = usersSheet.getDataRange().getValues();
var newHash = hashCode_(newPassword);
var updated = false;
for (var j = 1; j < uData.length; j++) {
if (uData[j][2] && uData[j][2].toString().trim().toLowerCase() === email.trim().toLowerCase()) {
usersSheet.getRange(j + 1, 4).setValue(newHash);
usersSheet.getRange(j + 1, 5).setValue('ACTIVE');
updated = true;
break;
}
}
if (updated) {
return { success: true, message: 'Wachtwoord succesvol gewijzigd! Je kunt nu direct inloggen.' };
} else {
return { success: false, message: 'E-mailadres niet gevonden in de gebruikerslijst.' };
}
} catch(e) {
return { success: false, message: 'Fout bij resetten wachtwoord: ' + e.toString() };
}
}
// --- G20 DAGBOEK & VRAGEN ---
function g20GetQuestions() {
try {
var ss = getActiveSpreadsheet_();
var sheet = ss.getSheetByName('G20_Questions');
if (!sheet || sheet.getLastRow() <= 1) {
return { success: true, questions: getDefaultQuestions_() };
}
var data = sheet.getDataRange().getValues();
var questions = [];
for (var i = 1; i < data.length; i++) {
var code = data[i][1] ? data[i][1].toString().trim() : '';
if (code.indexOf('G') !== 0) {
code = 'G' + data[i][0] + '_' + code;
}
questions.push({
number: data[i][0],
code: code,
title: data[i][2],
emoji: data[i][3],
question: data[i][4],
reflection: data[i][5]
});
}
return { success: true, questions: questions };
} catch(e) {
return { success: true, questions: getDefaultQuestions_() };
}
}
function g20GetDaily(userId, datum) {
try {
var ss = getActiveSpreadsheet_();
var sheet = ss.getSheetByName('G20_Daily');
if (!sheet) return { success: false };
var data = sheet.getDataRange().getValues();
if (data.length <= 1) return { success: false };
var headers = data[0];
var targetDateStr = datum.toString().substring(0, 10);
var colUser = headers.indexOf('UserID');
var colDate = headers.indexOf('Datum');
for (var i = 1; i < data.length; i++) {
var rowUser = colUser !== -1 ? data[i][colUser] : data[i][1];
var rowDate = colDate !== -1 ? data[i][colDate] : data[i][2];
var formattedRowDate = '';
if (rowDate instanceof Date) {
formattedRowDate = Utilities.formatDate(rowDate, Session.getScriptTimeZone(), 'yyyy-MM-dd');
} else if (rowDate) {
formattedRowDate = rowDate.toString().substring(0, 10);
}
if (rowUser === userId && formattedRowDate === targetDateStr) {
var record = {};
for (var h = 0; h < headers.length; h++) {
record[headers[h]] = data[i][h];
}
return { success: true, data: record };
}
}
return { success: false };
} catch(e) {
return { success: false, message: 'Fout bij ophalen dagboek: ' + e.toString() };
}
}
function g20SaveDaily(payload) {
try {
var ss = getActiveSpreadsheet_();
var sheet = ss.getSheetByName('G20_Daily');
if (!sheet) {
setupG20();
sheet = ss.getSheetByName('G20_Daily');
}
var data = sheet.getDataRange().getValues();
var headers = data[0];
var targetRow = -1;
var targetDateStr = payload.Datum ? payload.Datum.toString().substring(0, 10) : new Date().toISOString().substring(0, 10);
var colUser = headers.indexOf('UserID');
var colDate = headers.indexOf('Datum');
for (var i = 1; i < data.length; i++) {
var rowUser = colUser !== -1 ? data[i][colUser] : data[i][1];
var rowDate = colDate !== -1 ? data[i][colDate] : data[i][2];
var formattedRowDate = '';
if (rowDate instanceof Date) {
formattedRowDate = Utilities.formatDate(rowDate, Session.getScriptTimeZone(), 'yyyy-MM-dd');
} else if (rowDate) {
formattedRowDate = rowDate.toString().substring(0, 10);
}
if (rowUser === payload.UserID && formattedRowDate === targetDateStr) {
targetRow = i + 1;
break;
}
}
var rowData = [];
for (var h = 0; h < headers.length; h++) {
var colName = headers[h];
if (colName === 'RecordID') {
rowData.push(targetRow > 0 ? data[targetRow - 1][h] : 'REC_' + new Date().getTime());
} else if (colName === 'UserID') {
rowData.push(payload.UserID);
} else if (colName === 'Datum') {
rowData.push(targetDateStr);
} else if (colName === 'CreatedAt') {
rowData.push(targetRow > 0 ? data[targetRow - 1][h] : new Date());
} else if (colName === 'UpdatedAt') {
rowData.push(new Date());
} else {
var val = '';
if (payload[colName] !== undefined) {
val = payload[colName];
} else if (colName.indexOf('G') === 0) {
var parts = colName.split('_');
var gPrefix = parts[0];
if (payload[gPrefix] !== undefined) {
val = payload[gPrefix];
} else {
for (var key in payload) {
if (key.indexOf(gPrefix) !== -1 || key.toLowerCase() === colName.toLowerCase()) {
val = payload[key];
break;
}
}
}
} else {
for (var key in payload) {
if (key.toLowerCase() === colName.toLowerCase()) {
val = payload[key];
break;
}
}
}
rowData.push(val !== undefined && val !== null ? val : '');
}
}
if (targetRow > 0) {
sheet.getRange(targetRow, 1, 1, rowData.length).setValues([rowData]);
} else {
sheet.appendRow(rowData);
}
return { success: true, message: 'G20 dagboek succesvol opgeslagen in alle kolommen!' };
} catch(e) {
return { success: false, message: 'Fout bij opslaan dagboek: ' + e.toString() };
}
}
function g20GetUserHistory(userId) {
try {
var ss = getActiveSpreadsheet_();
var sheet = ss.getSheetByName('G20_Daily');
if (!sheet) return { success: true, history: [] };
var data = sheet.getDataRange().getValues();
if (data.length <= 1) return { success: true, history: [] };
var headers = data[0];
var colUser = headers.indexOf('UserID');
var history = [];
for (var i = 1; i < data.length; i++) {
var rowUser = colUser !== -1 ? data[i][colUser] : data[i][1];
if (rowUser === userId) {
var rowObj = {};
for (var h = 0; h < headers.length; h++) {
rowObj[headers[h]] = data[i][h];
}
history.push(rowObj);
}
}
return { success: true, history: history };
} catch(e) {
return { success: false, message: 'Fout bij ophalen geschiedenis: ' + e.toString() };
}
}
// --- DELEN VAN PERIODES (DAG, WEEK, MAAND, JAAR) ---
function g20GrantAccess(ownerEmail, viewerEmail, periodType, startDate, endDate) {
try {
var ss = getActiveSpreadsheet_();
var sheet = ss.getSheetByName('G20_Sharing');
if (!sheet) {
setupG20();
sheet = ss.getSheetByName('G20_Sharing');
}
var shareId = 'SHARE_' + new Date().getTime();
sheet.appendRow([shareId, ownerEmail.trim().toLowerCase(), viewerEmail.trim().toLowerCase(), periodType, startDate || '', endDate || '', new Date()]);
return { success: true, message: 'Toegang succesvol gedeeld (' + periodType + ').' };
} catch(e) {
return { success: false, message: 'Fout bij verlenen toegang: ' + e.toString() };
}
}
function g20CheckAccess(ownerEmail, viewerEmail, targetDateStr) {
try {
var ss = getActiveSpreadsheet_();
var sheet = ss.getSheetByName('G20_Sharing');
if (!sheet) return false;
var data = sheet.getDataRange().getValues();
var targetDate = new Date(targetDateStr);
for (var i = 1; i < data.length; i++) {
if (data[i][1].toString().toLowerCase() === ownerEmail.toLowerCase() &&
data[i][2].toString().toLowerCase() === viewerEmail.toLowerCase()) {
var periodType = data[i][3]; // 'day', 'week', 'month', 'year', 'all'
var start = data[i][4] ? new Date(data[i][4]) : null;
var end = data[i][5] ? new Date(data[i][5]) : null;
if (periodType === 'all') return true;
if (start && end && targetDate >= start && targetDate <= end) return true;
}
}
return false;
} catch(e) {
return false;
}
}
// --- EXPORT & IMPORT FUNCTIES (MD, TXT, CSV, JSON BACKUP) ---
function g20ExportData(userEmail, format, targetDate) {
try {
var ss = getActiveSpreadsheet_();
var dailySheet = ss.getSheetByName('G20_Daily');
var historyRes = g20GetUserHistoryByEmail_(userEmail);
if (format === 'json') {
var backupData = {
app: 'G20 NextLevel',
userEmail: userEmail,
exportDate: new Date(),
questions: g20GetQuestions().questions,
history: historyRes
};
return {
success: true,
filename: 'G20_Backup_' + userEmail.replace('@', '_') + '.json',
content: JSON.stringify(backupData, null, 2),
mime: 'application/json'
};
}
if (format === 'csv') {
var data = dailySheet.getDataRange().getValues();
var filtered = data.filter(function(row, idx) { return idx === 0 || row[1] === userEmail || row[2] === userEmail; });
var csvString = filtered.map(function(row) { return row.join(','); }).join('\n');
return {
success: true,
filename: 'G20_Antwoorden_' + userEmail + '.csv',
content: csvString,
mime: 'text/csv'
};
}
// Zoek specifieke dag voor MD of TXT
var entry = historyRes.find(function(h) {
var dStr = h.Datum instanceof Date ? Utilities.formatDate(h.Datum, Session.getScriptTimeZone(), 'yyyy-MM-dd') : h.Datum.toString().substring(0, 10);
return dStr === targetDate;
});
if (!entry) {
return { success: false, message: 'Geen antwoorden gevonden voor datum ' + targetDate };
}
var questions = g20GetQuestions().questions;
if (format === 'markdown' || format === 'md') {
var md = '# G20 Reflectie — ' + targetDate + '\n\n';
md += '*Gebruiker:* ' + userEmail + '\n\n---\n\n';
questions.forEach(function(q) {
var ans = entry[q.code] || '(Geen antwoord)';
md += '### ' + q.emoji + ' ' + q.title + '\n';
md += '> *' + q.question + '*\n\n';
md += '**Antwoord:** ' + ans + '\n\n---\n\n';
});
return { success: true, filename: 'G20_Reflectie_' + targetDate + '.md', content: md, mime: 'text/markdown' };
}
if (format === 'txt') {
var txt = 'G20 REFLECTIE — ' + targetDate + '\n========================================\n\n';
questions.forEach(function(q) {
var ans = entry[q.code] || '(Geen antwoord)';
txt += q.emoji + ' ' + q.title + ' (' + q.question + ')\n';
txt += '-> ' + ans + '\n\n';
});
return { success: true, filename: 'G20_Reflectie_' + targetDate + '.txt', content: txt, mime: 'text/plain' };
}
return { success: false, message: 'Onbekend exportformaat.' };
} catch(e) {
return { success: false, message: 'Fout bij exporteren: ' + e.toString() };
}
}
function g20ImportBackup(jsonString, userEmail) {
try {
var data = JSON.parse(jsonString);
if (!data.history || !Array.isArray(data.history)) {
return { success: false, message: 'Ongeldig backupbestand: geen geschiedenis gevonden.' };
}
data.history.forEach(function(item) {
item.UserID = item.UserID || userEmail;
g20SaveDaily(item);
});
return { success: true, message: 'Backup succesvol geïmporteerd in je Google Sheet!' };
} catch(e) {
return { success: false, message: 'Fout bij importeren backup: ' + e.toString() };
}
}
function g20GetUserHistoryByEmail_(email) {
var ss = getActiveSpreadsheet_();
var sheet = ss.getSheetByName('G20_Daily');
if (!sheet) return [];
var data = sheet.getDataRange().getValues();
if (data.length <= 1) return [];
var headers = data[0];
var history = [];
for (var i = 1; i < data.length; i++) {
var rowObj = {};
for (var h = 0; h < headers.length; h++) {
rowObj[headers[h]] = data[i][h];
}
if (rowObj.UserID === email || (rowObj.Datum && data[i].join(',').indexOf(email) !== -1)) {
history.push(rowObj);
}
}
return history;
}
// --- KAARTEN & PROFIEL ---
function g20GetCards() {
try {
var ss = getActiveSpreadsheet_();
var sheet = ss.getSheetByName('G20_Cards');
if (!sheet) return { success: false, message: 'Geen kaarten gevonden.' };
var data = sheet.getDataRange().getValues();
var cards = [];
for (var i = 1; i < data.length; i++) {
cards.push({
cardId: data[i][0],
category: data[i][1],
title: data[i][2],
description: data[i][4] || data[i][3]
});
}
return { success: true, cards: cards };
} catch(e) {
return { success: false, message: e.toString() };
}
}
function g20SaveProfile(userId, profileData) {
try {
var ss = getActiveSpreadsheet_();
var sheet = ss.getSheetByName('G20_Users');
var data = sheet.getDataRange().getValues();
for (var i = 1; i < data.length; i++) {
if (data[i][0] === userId) {
sheet.getRange(i + 1, 2).setValue(profileData.name);
sheet.getRange(i + 1, 8).setValue(profileData.quote);
sheet.getRange(i + 1, 9).setValue(profileData.favoriteTheme);
return { success: true, message: 'Profiel opgeslagen!' };
}
}
return { success: false, message: 'Gebruiker niet gevonden.' };
} catch(e) {
return { success: false, message: 'Fout bij opslaan profiel: ' + e.toString() };
}
}
// --- HASHING ---
function hashCode_(str) {
if (!str) return '';
var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, str, Utilities.Charset.UTF_8);
var output = '';
for (var i = 0; i < rawHash.length; i++) {
var byteValue = rawHash[i];
if (byteValue < 0) byteValue += 256;
var byteString = byteValue.toString(16);
if (byteString.length == 1) byteString = '0' + byteString;
output += byteString;
}
return output;
}
function oldHashCode_(str) {
var hash = 0;
if (!str || str.length === 0) return hash.toString();
for (var i = 0; i < str.length; i++) {
var char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return hash.toString();
} en voeg daarbij dus het opslaan toe van de kolommen, G1_GeluksbesefG2_GevoelensG3_GedroomdG4_GemisG5_GewenstG6_GehooptG7_GedachtenG8_GedragG9_GezegdG10_GedaanG11_GeschrevenG12_GelezenG13_GeleerdG14_GewetenG15_GehoordG16_GezienG17_GeproefdG18_GerokenG19_GedachtOverG20_GeluksgetalInzichtMooisteMomentMorgenStap zodra iemand iets invult is het belangrijk dat dat opgeslagen kan worden onder de datum en user en tijdstempel, als men dan terug zoekt naar die dag dan wil men alle data van die sheet kunnen vinden op die dag en bewerken als dat nog nodig is, exporteren naar txt pdf en csv per dag is ook nodig en exporteren van alle dagen die ingevuld zijn inclusief persoonlijke settings, profielnaam, etc is nodig naar export json en geef de JSON een mogelijkheid om deze in te laden als user 1 deze opslaat en aan user 2 geeft als bestand, dat User 2 dit kan lezen en dus de dagboek invullingen van user 1 kan lezen met daarbij de informatie van het profiel. De user 1 mag kiezen alles exporteren of alleen dag, maand, week, datums selecteren, of bijvoorbeeld het hele jaar.