Auto commit: 2025-09-18T16:05:59.956Z
This commit is contained in:
parent
f0ac3b80cd
commit
9658341031
File diff suppressed because one or more lines are too long
258
backend/src/db/api/books.js
Normal file
258
backend/src/db/api/books.js
Normal file
@ -0,0 +1,258 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class BooksDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const books = await db.books.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
name: data.name || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
return books;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const booksData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
name: item.name || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const books = await db.books.bulkCreate(booksData, { transaction });
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return books;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const books = await db.books.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.name !== undefined) updatePayload.name = data.name;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await books.update(updatePayload, { transaction });
|
||||
|
||||
return books;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const books = await db.books.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of books) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of books) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return books;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const books = await db.books.findByPk(id, options);
|
||||
|
||||
await books.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await books.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return books;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const books = await db.books.findOne({ where }, { transaction });
|
||||
|
||||
if (!books) {
|
||||
return books;
|
||||
}
|
||||
|
||||
const output = books.get({ plain: true });
|
||||
|
||||
output.readings_bookref = await books.getReadings_bookref({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.readings_book_select = await books.getReadings_book_select({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.chapters_book = await books.getChapters_book({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('books', 'name', filter.name),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.books.findAndCountAll(queryOptions);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset) {
|
||||
let where = {};
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('books', 'name', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.books.findAll({
|
||||
attributes: ['id', 'name'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['name', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.name,
|
||||
}));
|
||||
}
|
||||
};
|
||||
315
backend/src/db/api/chapters.js
Normal file
315
backend/src/db/api/chapters.js
Normal file
@ -0,0 +1,315 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class ChaptersDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const chapters = await db.chapters.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
number: data.number || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await chapters.setBook(data.book || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return chapters;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const chaptersData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
number: item.number || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const chapters = await db.chapters.bulkCreate(chaptersData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return chapters;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const chapters = await db.chapters.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.number !== undefined) updatePayload.number = data.number;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await chapters.update(updatePayload, { transaction });
|
||||
|
||||
if (data.book !== undefined) {
|
||||
await chapters.setBook(
|
||||
data.book,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
return chapters;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const chapters = await db.chapters.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of chapters) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of chapters) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return chapters;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const chapters = await db.chapters.findByPk(id, options);
|
||||
|
||||
await chapters.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await chapters.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return chapters;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const chapters = await db.chapters.findOne({ where }, { transaction });
|
||||
|
||||
if (!chapters) {
|
||||
return chapters;
|
||||
}
|
||||
|
||||
const output = chapters.get({ plain: true });
|
||||
|
||||
output.readings_chapterref = await chapters.getReadings_chapterref({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.readings_chapter_select = await chapters.getReadings_chapter_select({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.book = await chapters.getBook({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
{
|
||||
model: db.books,
|
||||
as: 'book',
|
||||
|
||||
where: filter.book
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.book
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.book
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.numberRange) {
|
||||
const [start, end] = filter.numberRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
number: {
|
||||
...where.number,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
number: {
|
||||
...where.number,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.chapters.findAndCountAll(queryOptions);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset) {
|
||||
let where = {};
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('chapters', 'number', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.chapters.findAll({
|
||||
attributes: ['id', 'number'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['number', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.number,
|
||||
}));
|
||||
}
|
||||
};
|
||||
@ -191,7 +191,7 @@ module.exports = class NotesDBApi {
|
||||
},
|
||||
},
|
||||
{
|
||||
book: {
|
||||
tags: {
|
||||
[Op.or]: filter.reading
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
|
||||
@ -21,6 +21,7 @@ module.exports = class ReadingsDBApi {
|
||||
verses: data.verses || null,
|
||||
translation: data.translation || null,
|
||||
notes: data.notes || null,
|
||||
tags: data.tags || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
@ -32,6 +33,38 @@ module.exports = class ReadingsDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await readings.setBookref(data.bookref || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await readings.setChapterref(data.chapterref || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await readings.setTranslationref(data.translationref || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await readings.setBook_select(data.book_select || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await readings.setChapter_select(data.chapter_select || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await readings.setTranslation_select(data.translation_select || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await readings.setVersesref(data.versesref || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await readings.setVerses_select(data.verses_select || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return readings;
|
||||
}
|
||||
|
||||
@ -49,6 +82,7 @@ module.exports = class ReadingsDBApi {
|
||||
verses: item.verses || null,
|
||||
translation: item.translation || null,
|
||||
notes: item.notes || null,
|
||||
tags: item.tags || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
@ -86,6 +120,8 @@ module.exports = class ReadingsDBApi {
|
||||
|
||||
if (data.notes !== undefined) updatePayload.notes = data.notes;
|
||||
|
||||
if (data.tags !== undefined) updatePayload.tags = data.tags;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await readings.update(updatePayload, { transaction });
|
||||
@ -98,6 +134,62 @@ module.exports = class ReadingsDBApi {
|
||||
);
|
||||
}
|
||||
|
||||
if (data.bookref !== undefined) {
|
||||
await readings.setBookref(
|
||||
data.bookref,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.chapterref !== undefined) {
|
||||
await readings.setChapterref(
|
||||
data.chapterref,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.translationref !== undefined) {
|
||||
await readings.setTranslationref(
|
||||
data.translationref,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.book_select !== undefined) {
|
||||
await readings.setBook_select(
|
||||
data.book_select,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.chapter_select !== undefined) {
|
||||
await readings.setChapter_select(
|
||||
data.chapter_select,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.translation_select !== undefined) {
|
||||
await readings.setTranslation_select(
|
||||
data.translation_select,
|
||||
|
||||
{ transaction },
|
||||
);
|
||||
}
|
||||
|
||||
if (data.versesref !== undefined) {
|
||||
await readings.setVersesref(data.versesref, { transaction });
|
||||
}
|
||||
|
||||
if (data.verses_select !== undefined) {
|
||||
await readings.setVerses_select(data.verses_select, { transaction });
|
||||
}
|
||||
|
||||
return readings;
|
||||
}
|
||||
|
||||
@ -167,6 +259,38 @@ module.exports = class ReadingsDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.bookref = await readings.getBookref({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.chapterref = await readings.getChapterref({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.versesref = await readings.getVersesref({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.translationref = await readings.getTranslationref({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.book_select = await readings.getBook_select({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.chapter_select = await readings.getChapter_select({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.verses_select = await readings.getVerses_select({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.translation_select = await readings.getTranslation_select({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@ -208,6 +332,174 @@ module.exports = class ReadingsDBApi {
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.books,
|
||||
as: 'bookref',
|
||||
|
||||
where: filter.bookref
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.bookref
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.bookref
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.chapters,
|
||||
as: 'chapterref',
|
||||
|
||||
where: filter.chapterref
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.chapterref
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
number: {
|
||||
[Op.or]: filter.chapterref
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.translations,
|
||||
as: 'translationref',
|
||||
|
||||
where: filter.translationref
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.translationref
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.translationref
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.books,
|
||||
as: 'book_select',
|
||||
|
||||
where: filter.book_select
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.book_select
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.book_select
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.chapters,
|
||||
as: 'chapter_select',
|
||||
|
||||
where: filter.chapter_select
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.chapter_select
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
number: {
|
||||
[Op.or]: filter.chapter_select
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.translations,
|
||||
as: 'translation_select',
|
||||
|
||||
where: filter.translation_select
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: filter.translation_select
|
||||
.split('|')
|
||||
.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: {
|
||||
[Op.or]: filter.translation_select
|
||||
.split('|')
|
||||
.map((term) => ({ [Op.iLike]: `%${term}%` })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
{
|
||||
model: db.verses,
|
||||
as: 'versesref',
|
||||
required: false,
|
||||
},
|
||||
|
||||
{
|
||||
model: db.verses,
|
||||
as: 'verses_select',
|
||||
required: false,
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
@ -246,6 +538,13 @@ module.exports = class ReadingsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.tags) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('readings', 'tags', filter.tags),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.calendarStart && filter.calendarEnd) {
|
||||
where = {
|
||||
...where,
|
||||
@ -319,6 +618,70 @@ module.exports = class ReadingsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.versesref) {
|
||||
const searchTerms = filter.versesref.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.verses,
|
||||
as: 'versesref_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
number: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.verses_select) {
|
||||
const searchTerms = filter.verses_select.split('|');
|
||||
|
||||
include = [
|
||||
{
|
||||
model: db.verses,
|
||||
as: 'verses_select_filter',
|
||||
required: searchTerms.length > 0,
|
||||
where:
|
||||
searchTerms.length > 0
|
||||
? {
|
||||
[Op.or]: [
|
||||
{
|
||||
id: {
|
||||
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
|
||||
},
|
||||
},
|
||||
{
|
||||
number: {
|
||||
[Op.or]: searchTerms.map((term) => ({
|
||||
[Op.iLike]: `%${term}%`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
...include,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
@ -381,22 +744,22 @@ module.exports = class ReadingsDBApi {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('readings', 'book', query),
|
||||
Utils.ilike('readings', 'tags', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.readings.findAll({
|
||||
attributes: ['id', 'book'],
|
||||
attributes: ['id', 'tags'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['book', 'ASC']],
|
||||
orderBy: [['tags', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.book,
|
||||
label: record.tags,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
267
backend/src/db/api/translations.js
Normal file
267
backend/src/db/api/translations.js
Normal file
@ -0,0 +1,267 @@
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class TranslationsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const translations = await db.translations.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
name: data.name || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
return translations;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const translationsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
name: item.name || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const translations = await db.translations.bulkCreate(translationsData, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
return translations;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const translations = await db.translations.findByPk(
|
||||
id,
|
||||
{},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.name !== undefined) updatePayload.name = data.name;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await translations.update(updatePayload, { transaction });
|
||||
|
||||
return translations;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const translations = await db.translations.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
},
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of translations) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of translations) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return translations;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const translations = await db.translations.findByPk(id, options);
|
||||
|
||||
await translations.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
},
|
||||
);
|
||||
|
||||
await translations.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return translations;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const translations = await db.translations.findOne(
|
||||
{ where },
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
if (!translations) {
|
||||
return translations;
|
||||
}
|
||||
|
||||
const output = translations.get({ plain: true });
|
||||
|
||||
output.readings_translationref =
|
||||
await translations.getReadings_translationref({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.readings_translation_select =
|
||||
await translations.getReadings_translation_select({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static async findAll(filter, options) {
|
||||
const limit = filter.limit || 0;
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [];
|
||||
|
||||
if (filter) {
|
||||
if (filter.id) {
|
||||
where = {
|
||||
...where,
|
||||
['id']: Utils.uuid(filter.id),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('translations', 'name', filter.name),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
active: filter.active === true || filter.active === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.createdAtRange) {
|
||||
const [start, end] = filter.createdAtRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
['createdAt']: {
|
||||
...where.createdAt,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
include,
|
||||
distinct: true,
|
||||
order:
|
||||
filter.field && filter.sort
|
||||
? [[filter.field, filter.sort]]
|
||||
: [['createdAt', 'desc']],
|
||||
transaction: options?.transaction,
|
||||
logging: console.log,
|
||||
};
|
||||
|
||||
if (!options?.countOnly) {
|
||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.translations.findAndCountAll(
|
||||
queryOptions,
|
||||
);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error executing query:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset) {
|
||||
let where = {};
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('translations', 'name', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.translations.findAll({
|
||||
attributes: ['id', 'name'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
offset: offset ? Number(offset) : undefined,
|
||||
orderBy: [['name', 'ASC']],
|
||||
});
|
||||
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
label: record.name,
|
||||
}));
|
||||
}
|
||||
};
|
||||
54
backend/src/db/migrations/1758211105293.js
Normal file
54
backend/src/db/migrations/1758211105293.js
Normal file
@ -0,0 +1,54 @@
|
||||
module.exports = {
|
||||
/**
|
||||
* @param {QueryInterface} queryInterface
|
||||
* @param {Sequelize} Sequelize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async up(queryInterface, Sequelize) {
|
||||
/**
|
||||
* @type {Transaction}
|
||||
*/
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
await queryInterface.addColumn(
|
||||
'readings',
|
||||
'book_selectId',
|
||||
{
|
||||
type: Sequelize.DataTypes.UUID,
|
||||
|
||||
references: {
|
||||
model: 'books',
|
||||
key: 'id',
|
||||
},
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {QueryInterface} queryInterface
|
||||
* @param {Sequelize} Sequelize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async down(queryInterface, Sequelize) {
|
||||
/**
|
||||
* @type {Transaction}
|
||||
*/
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
await queryInterface.removeColumn('readings', 'book_selectId', {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
};
|
||||
54
backend/src/db/migrations/1758211132842.js
Normal file
54
backend/src/db/migrations/1758211132842.js
Normal file
@ -0,0 +1,54 @@
|
||||
module.exports = {
|
||||
/**
|
||||
* @param {QueryInterface} queryInterface
|
||||
* @param {Sequelize} Sequelize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async up(queryInterface, Sequelize) {
|
||||
/**
|
||||
* @type {Transaction}
|
||||
*/
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
await queryInterface.addColumn(
|
||||
'readings',
|
||||
'chapter_selectId',
|
||||
{
|
||||
type: Sequelize.DataTypes.UUID,
|
||||
|
||||
references: {
|
||||
model: 'chapters',
|
||||
key: 'id',
|
||||
},
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {QueryInterface} queryInterface
|
||||
* @param {Sequelize} Sequelize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async down(queryInterface, Sequelize) {
|
||||
/**
|
||||
* @type {Transaction}
|
||||
*/
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
await queryInterface.removeColumn('readings', 'chapter_selectId', {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
};
|
||||
36
backend/src/db/migrations/1758211158025.js
Normal file
36
backend/src/db/migrations/1758211158025.js
Normal file
@ -0,0 +1,36 @@
|
||||
module.exports = {
|
||||
/**
|
||||
* @param {QueryInterface} queryInterface
|
||||
* @param {Sequelize} Sequelize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async up(queryInterface, Sequelize) {
|
||||
/**
|
||||
* @type {Transaction}
|
||||
*/
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {QueryInterface} queryInterface
|
||||
* @param {Sequelize} Sequelize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async down(queryInterface, Sequelize) {
|
||||
/**
|
||||
* @type {Transaction}
|
||||
*/
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
};
|
||||
54
backend/src/db/migrations/1758211187600.js
Normal file
54
backend/src/db/migrations/1758211187600.js
Normal file
@ -0,0 +1,54 @@
|
||||
module.exports = {
|
||||
/**
|
||||
* @param {QueryInterface} queryInterface
|
||||
* @param {Sequelize} Sequelize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async up(queryInterface, Sequelize) {
|
||||
/**
|
||||
* @type {Transaction}
|
||||
*/
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
await queryInterface.addColumn(
|
||||
'readings',
|
||||
'translation_selectId',
|
||||
{
|
||||
type: Sequelize.DataTypes.UUID,
|
||||
|
||||
references: {
|
||||
model: 'translations',
|
||||
key: 'id',
|
||||
},
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {QueryInterface} queryInterface
|
||||
* @param {Sequelize} Sequelize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async down(queryInterface, Sequelize) {
|
||||
/**
|
||||
* @type {Transaction}
|
||||
*/
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
await queryInterface.removeColumn('readings', 'translation_selectId', {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
};
|
||||
47
backend/src/db/migrations/1758211229331.js
Normal file
47
backend/src/db/migrations/1758211229331.js
Normal file
@ -0,0 +1,47 @@
|
||||
module.exports = {
|
||||
/**
|
||||
* @param {QueryInterface} queryInterface
|
||||
* @param {Sequelize} Sequelize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async up(queryInterface, Sequelize) {
|
||||
/**
|
||||
* @type {Transaction}
|
||||
*/
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
await queryInterface.addColumn(
|
||||
'readings',
|
||||
'tags',
|
||||
{
|
||||
type: Sequelize.DataTypes.TEXT,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {QueryInterface} queryInterface
|
||||
* @param {Sequelize} Sequelize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async down(queryInterface, Sequelize) {
|
||||
/**
|
||||
* @type {Transaction}
|
||||
*/
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
await queryInterface.removeColumn('readings', 'tags', { transaction });
|
||||
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
};
|
||||
73
backend/src/db/models/books.js
Normal file
73
backend/src/db/models/books.js
Normal file
@ -0,0 +1,73 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const books = sequelize.define(
|
||||
'books',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
name: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
books.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.books.hasMany(db.readings, {
|
||||
as: 'readings_bookref',
|
||||
foreignKey: {
|
||||
name: 'bookrefId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.books.hasMany(db.readings, {
|
||||
as: 'readings_book_select',
|
||||
foreignKey: {
|
||||
name: 'book_selectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.books.hasMany(db.chapters, {
|
||||
as: 'chapters_book',
|
||||
foreignKey: {
|
||||
name: 'bookId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.books.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.books.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return books;
|
||||
};
|
||||
73
backend/src/db/models/chapters.js
Normal file
73
backend/src/db/models/chapters.js
Normal file
@ -0,0 +1,73 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const chapters = sequelize.define(
|
||||
'chapters',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
number: {
|
||||
type: DataTypes.INTEGER,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
chapters.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.chapters.hasMany(db.readings, {
|
||||
as: 'readings_chapterref',
|
||||
foreignKey: {
|
||||
name: 'chapterrefId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.chapters.hasMany(db.readings, {
|
||||
as: 'readings_chapter_select',
|
||||
foreignKey: {
|
||||
name: 'chapter_selectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.chapters.belongsTo(db.books, {
|
||||
as: 'book',
|
||||
foreignKey: {
|
||||
name: 'bookId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.chapters.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.chapters.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return chapters;
|
||||
};
|
||||
@ -38,6 +38,10 @@ module.exports = function (sequelize, DataTypes) {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
tags: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
@ -52,6 +56,42 @@ module.exports = function (sequelize, DataTypes) {
|
||||
);
|
||||
|
||||
readings.associate = (db) => {
|
||||
db.readings.belongsToMany(db.verses, {
|
||||
as: 'versesref',
|
||||
foreignKey: {
|
||||
name: 'readings_versesrefId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'readingsVersesrefVerses',
|
||||
});
|
||||
|
||||
db.readings.belongsToMany(db.verses, {
|
||||
as: 'versesref_filter',
|
||||
foreignKey: {
|
||||
name: 'readings_versesrefId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'readingsVersesrefVerses',
|
||||
});
|
||||
|
||||
db.readings.belongsToMany(db.verses, {
|
||||
as: 'verses_select',
|
||||
foreignKey: {
|
||||
name: 'readings_verses_selectId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'readingsVerses_selectVerses',
|
||||
});
|
||||
|
||||
db.readings.belongsToMany(db.verses, {
|
||||
as: 'verses_select_filter',
|
||||
foreignKey: {
|
||||
name: 'readings_verses_selectId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'readingsVerses_selectVerses',
|
||||
});
|
||||
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.readings.hasMany(db.notes, {
|
||||
@ -72,6 +112,54 @@ module.exports = function (sequelize, DataTypes) {
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.readings.belongsTo(db.books, {
|
||||
as: 'bookref',
|
||||
foreignKey: {
|
||||
name: 'bookrefId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.readings.belongsTo(db.chapters, {
|
||||
as: 'chapterref',
|
||||
foreignKey: {
|
||||
name: 'chapterrefId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.readings.belongsTo(db.translations, {
|
||||
as: 'translationref',
|
||||
foreignKey: {
|
||||
name: 'translationrefId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.readings.belongsTo(db.books, {
|
||||
as: 'book_select',
|
||||
foreignKey: {
|
||||
name: 'book_selectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.readings.belongsTo(db.chapters, {
|
||||
as: 'chapter_select',
|
||||
foreignKey: {
|
||||
name: 'chapter_selectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.readings.belongsTo(db.translations, {
|
||||
as: 'translation_select',
|
||||
foreignKey: {
|
||||
name: 'translation_selectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.readings.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
65
backend/src/db/models/translations.js
Normal file
65
backend/src/db/models/translations.js
Normal file
@ -0,0 +1,65 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const translations = sequelize.define(
|
||||
'translations',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
name: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
freezeTableName: true,
|
||||
},
|
||||
);
|
||||
|
||||
translations.associate = (db) => {
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
db.translations.hasMany(db.readings, {
|
||||
as: 'readings_translationref',
|
||||
foreignKey: {
|
||||
name: 'translationrefId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
db.translations.hasMany(db.readings, {
|
||||
as: 'readings_translation_select',
|
||||
foreignKey: {
|
||||
name: 'translation_selectId',
|
||||
},
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
//end loop
|
||||
|
||||
db.translations.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.translations.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return translations;
|
||||
};
|
||||
@ -7,6 +7,14 @@ const Progress = db.progress;
|
||||
|
||||
const Readings = db.readings;
|
||||
|
||||
const Books = db.books;
|
||||
|
||||
const Chapters = db.chapters;
|
||||
|
||||
const Verses = db.verses;
|
||||
|
||||
const Translations = db.translations;
|
||||
|
||||
const NotesData = [
|
||||
{
|
||||
content: 'Reflecting on the creation story.',
|
||||
@ -37,6 +45,16 @@ const NotesData = [
|
||||
|
||||
// type code here for "relation_one" field
|
||||
},
|
||||
|
||||
{
|
||||
content: 'Blessings of the kingdom.',
|
||||
|
||||
tags: 'Beatitudes,blessings',
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
},
|
||||
];
|
||||
|
||||
const ProgressData = [
|
||||
@ -69,6 +87,16 @@ const ProgressData = [
|
||||
|
||||
// type code here for "relation_one" field
|
||||
},
|
||||
|
||||
{
|
||||
book: 'Matthew',
|
||||
|
||||
chapter: 5,
|
||||
|
||||
read: true,
|
||||
|
||||
// type code here for "relation_one" field
|
||||
},
|
||||
];
|
||||
|
||||
const ReadingsData = [
|
||||
@ -86,6 +114,24 @@ const ReadingsData = [
|
||||
notes: 'In the beginning, God created the heavens and the earth.',
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_many" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_many" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
tags: 'Max Planck',
|
||||
},
|
||||
|
||||
{
|
||||
@ -102,6 +148,24 @@ const ReadingsData = [
|
||||
notes: 'Moses and the burning bush.',
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_many" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_many" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
tags: 'Francis Galton',
|
||||
},
|
||||
|
||||
{
|
||||
@ -118,6 +182,138 @@ const ReadingsData = [
|
||||
notes: 'The Lord is my shepherd.',
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_many" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_many" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
tags: 'Comte de Buffon',
|
||||
},
|
||||
|
||||
{
|
||||
date: new Date('2023-10-04T08:00:00Z'),
|
||||
|
||||
book: 'Matthew',
|
||||
|
||||
chapter: 5,
|
||||
|
||||
verses: '1-12',
|
||||
|
||||
translation: 'NKJV',
|
||||
|
||||
notes: 'The Beatitudes.',
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_many" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
// type code here for "relation_many" field
|
||||
|
||||
// type code here for "relation_one" field
|
||||
|
||||
tags: 'Albert Einstein',
|
||||
},
|
||||
];
|
||||
|
||||
const BooksData = [
|
||||
{
|
||||
name: 'Linus Pauling',
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Marie Curie',
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Alfred Binet',
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Konrad Lorenz',
|
||||
},
|
||||
];
|
||||
|
||||
const ChaptersData = [
|
||||
{
|
||||
number: 4,
|
||||
|
||||
// type code here for "relation_one" field
|
||||
},
|
||||
|
||||
{
|
||||
number: 3,
|
||||
|
||||
// type code here for "relation_one" field
|
||||
},
|
||||
|
||||
{
|
||||
number: 9,
|
||||
|
||||
// type code here for "relation_one" field
|
||||
},
|
||||
|
||||
{
|
||||
number: 1,
|
||||
|
||||
// type code here for "relation_one" field
|
||||
},
|
||||
];
|
||||
|
||||
const VersesData = [
|
||||
{
|
||||
number: 2,
|
||||
},
|
||||
|
||||
{
|
||||
number: 3,
|
||||
},
|
||||
|
||||
{
|
||||
number: 5,
|
||||
},
|
||||
|
||||
{
|
||||
number: 5,
|
||||
},
|
||||
];
|
||||
|
||||
const TranslationsData = [
|
||||
{
|
||||
name: 'Konrad Lorenz',
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Enrico Fermi',
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Paul Ehrlich',
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Sheldon Glashow',
|
||||
},
|
||||
];
|
||||
|
||||
@ -156,6 +352,17 @@ async function associateNoteWithReading() {
|
||||
if (Note2?.setReading) {
|
||||
await Note2.setReading(relatedReading2);
|
||||
}
|
||||
|
||||
const relatedReading3 = await Readings.findOne({
|
||||
offset: Math.floor(Math.random() * (await Readings.count())),
|
||||
});
|
||||
const Note3 = await Notes.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 3,
|
||||
});
|
||||
if (Note3?.setReading) {
|
||||
await Note3.setReading(relatedReading3);
|
||||
}
|
||||
}
|
||||
|
||||
async function associateNoteWithUser() {
|
||||
@ -191,6 +398,17 @@ async function associateNoteWithUser() {
|
||||
if (Note2?.setUser) {
|
||||
await Note2.setUser(relatedUser2);
|
||||
}
|
||||
|
||||
const relatedUser3 = await Users.findOne({
|
||||
offset: Math.floor(Math.random() * (await Users.count())),
|
||||
});
|
||||
const Note3 = await Notes.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 3,
|
||||
});
|
||||
if (Note3?.setUser) {
|
||||
await Note3.setUser(relatedUser3);
|
||||
}
|
||||
}
|
||||
|
||||
async function associateProgressWithUser() {
|
||||
@ -226,6 +444,17 @@ async function associateProgressWithUser() {
|
||||
if (Progress2?.setUser) {
|
||||
await Progress2.setUser(relatedUser2);
|
||||
}
|
||||
|
||||
const relatedUser3 = await Users.findOne({
|
||||
offset: Math.floor(Math.random() * (await Users.count())),
|
||||
});
|
||||
const Progress3 = await Progress.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 3,
|
||||
});
|
||||
if (Progress3?.setUser) {
|
||||
await Progress3.setUser(relatedUser3);
|
||||
}
|
||||
}
|
||||
|
||||
async function associateReadingWithUser() {
|
||||
@ -261,6 +490,343 @@ async function associateReadingWithUser() {
|
||||
if (Reading2?.setUser) {
|
||||
await Reading2.setUser(relatedUser2);
|
||||
}
|
||||
|
||||
const relatedUser3 = await Users.findOne({
|
||||
offset: Math.floor(Math.random() * (await Users.count())),
|
||||
});
|
||||
const Reading3 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 3,
|
||||
});
|
||||
if (Reading3?.setUser) {
|
||||
await Reading3.setUser(relatedUser3);
|
||||
}
|
||||
}
|
||||
|
||||
async function associateReadingWithBookref() {
|
||||
const relatedBookref0 = await Books.findOne({
|
||||
offset: Math.floor(Math.random() * (await Books.count())),
|
||||
});
|
||||
const Reading0 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 0,
|
||||
});
|
||||
if (Reading0?.setBookref) {
|
||||
await Reading0.setBookref(relatedBookref0);
|
||||
}
|
||||
|
||||
const relatedBookref1 = await Books.findOne({
|
||||
offset: Math.floor(Math.random() * (await Books.count())),
|
||||
});
|
||||
const Reading1 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 1,
|
||||
});
|
||||
if (Reading1?.setBookref) {
|
||||
await Reading1.setBookref(relatedBookref1);
|
||||
}
|
||||
|
||||
const relatedBookref2 = await Books.findOne({
|
||||
offset: Math.floor(Math.random() * (await Books.count())),
|
||||
});
|
||||
const Reading2 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 2,
|
||||
});
|
||||
if (Reading2?.setBookref) {
|
||||
await Reading2.setBookref(relatedBookref2);
|
||||
}
|
||||
|
||||
const relatedBookref3 = await Books.findOne({
|
||||
offset: Math.floor(Math.random() * (await Books.count())),
|
||||
});
|
||||
const Reading3 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 3,
|
||||
});
|
||||
if (Reading3?.setBookref) {
|
||||
await Reading3.setBookref(relatedBookref3);
|
||||
}
|
||||
}
|
||||
|
||||
async function associateReadingWithChapterref() {
|
||||
const relatedChapterref0 = await Chapters.findOne({
|
||||
offset: Math.floor(Math.random() * (await Chapters.count())),
|
||||
});
|
||||
const Reading0 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 0,
|
||||
});
|
||||
if (Reading0?.setChapterref) {
|
||||
await Reading0.setChapterref(relatedChapterref0);
|
||||
}
|
||||
|
||||
const relatedChapterref1 = await Chapters.findOne({
|
||||
offset: Math.floor(Math.random() * (await Chapters.count())),
|
||||
});
|
||||
const Reading1 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 1,
|
||||
});
|
||||
if (Reading1?.setChapterref) {
|
||||
await Reading1.setChapterref(relatedChapterref1);
|
||||
}
|
||||
|
||||
const relatedChapterref2 = await Chapters.findOne({
|
||||
offset: Math.floor(Math.random() * (await Chapters.count())),
|
||||
});
|
||||
const Reading2 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 2,
|
||||
});
|
||||
if (Reading2?.setChapterref) {
|
||||
await Reading2.setChapterref(relatedChapterref2);
|
||||
}
|
||||
|
||||
const relatedChapterref3 = await Chapters.findOne({
|
||||
offset: Math.floor(Math.random() * (await Chapters.count())),
|
||||
});
|
||||
const Reading3 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 3,
|
||||
});
|
||||
if (Reading3?.setChapterref) {
|
||||
await Reading3.setChapterref(relatedChapterref3);
|
||||
}
|
||||
}
|
||||
|
||||
// Similar logic for "relation_many"
|
||||
|
||||
async function associateReadingWithTranslationref() {
|
||||
const relatedTranslationref0 = await Translations.findOne({
|
||||
offset: Math.floor(Math.random() * (await Translations.count())),
|
||||
});
|
||||
const Reading0 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 0,
|
||||
});
|
||||
if (Reading0?.setTranslationref) {
|
||||
await Reading0.setTranslationref(relatedTranslationref0);
|
||||
}
|
||||
|
||||
const relatedTranslationref1 = await Translations.findOne({
|
||||
offset: Math.floor(Math.random() * (await Translations.count())),
|
||||
});
|
||||
const Reading1 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 1,
|
||||
});
|
||||
if (Reading1?.setTranslationref) {
|
||||
await Reading1.setTranslationref(relatedTranslationref1);
|
||||
}
|
||||
|
||||
const relatedTranslationref2 = await Translations.findOne({
|
||||
offset: Math.floor(Math.random() * (await Translations.count())),
|
||||
});
|
||||
const Reading2 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 2,
|
||||
});
|
||||
if (Reading2?.setTranslationref) {
|
||||
await Reading2.setTranslationref(relatedTranslationref2);
|
||||
}
|
||||
|
||||
const relatedTranslationref3 = await Translations.findOne({
|
||||
offset: Math.floor(Math.random() * (await Translations.count())),
|
||||
});
|
||||
const Reading3 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 3,
|
||||
});
|
||||
if (Reading3?.setTranslationref) {
|
||||
await Reading3.setTranslationref(relatedTranslationref3);
|
||||
}
|
||||
}
|
||||
|
||||
async function associateReadingWithBook_select() {
|
||||
const relatedBook_select0 = await Books.findOne({
|
||||
offset: Math.floor(Math.random() * (await Books.count())),
|
||||
});
|
||||
const Reading0 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 0,
|
||||
});
|
||||
if (Reading0?.setBook_select) {
|
||||
await Reading0.setBook_select(relatedBook_select0);
|
||||
}
|
||||
|
||||
const relatedBook_select1 = await Books.findOne({
|
||||
offset: Math.floor(Math.random() * (await Books.count())),
|
||||
});
|
||||
const Reading1 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 1,
|
||||
});
|
||||
if (Reading1?.setBook_select) {
|
||||
await Reading1.setBook_select(relatedBook_select1);
|
||||
}
|
||||
|
||||
const relatedBook_select2 = await Books.findOne({
|
||||
offset: Math.floor(Math.random() * (await Books.count())),
|
||||
});
|
||||
const Reading2 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 2,
|
||||
});
|
||||
if (Reading2?.setBook_select) {
|
||||
await Reading2.setBook_select(relatedBook_select2);
|
||||
}
|
||||
|
||||
const relatedBook_select3 = await Books.findOne({
|
||||
offset: Math.floor(Math.random() * (await Books.count())),
|
||||
});
|
||||
const Reading3 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 3,
|
||||
});
|
||||
if (Reading3?.setBook_select) {
|
||||
await Reading3.setBook_select(relatedBook_select3);
|
||||
}
|
||||
}
|
||||
|
||||
async function associateReadingWithChapter_select() {
|
||||
const relatedChapter_select0 = await Chapters.findOne({
|
||||
offset: Math.floor(Math.random() * (await Chapters.count())),
|
||||
});
|
||||
const Reading0 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 0,
|
||||
});
|
||||
if (Reading0?.setChapter_select) {
|
||||
await Reading0.setChapter_select(relatedChapter_select0);
|
||||
}
|
||||
|
||||
const relatedChapter_select1 = await Chapters.findOne({
|
||||
offset: Math.floor(Math.random() * (await Chapters.count())),
|
||||
});
|
||||
const Reading1 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 1,
|
||||
});
|
||||
if (Reading1?.setChapter_select) {
|
||||
await Reading1.setChapter_select(relatedChapter_select1);
|
||||
}
|
||||
|
||||
const relatedChapter_select2 = await Chapters.findOne({
|
||||
offset: Math.floor(Math.random() * (await Chapters.count())),
|
||||
});
|
||||
const Reading2 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 2,
|
||||
});
|
||||
if (Reading2?.setChapter_select) {
|
||||
await Reading2.setChapter_select(relatedChapter_select2);
|
||||
}
|
||||
|
||||
const relatedChapter_select3 = await Chapters.findOne({
|
||||
offset: Math.floor(Math.random() * (await Chapters.count())),
|
||||
});
|
||||
const Reading3 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 3,
|
||||
});
|
||||
if (Reading3?.setChapter_select) {
|
||||
await Reading3.setChapter_select(relatedChapter_select3);
|
||||
}
|
||||
}
|
||||
|
||||
// Similar logic for "relation_many"
|
||||
|
||||
async function associateReadingWithTranslation_select() {
|
||||
const relatedTranslation_select0 = await Translations.findOne({
|
||||
offset: Math.floor(Math.random() * (await Translations.count())),
|
||||
});
|
||||
const Reading0 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 0,
|
||||
});
|
||||
if (Reading0?.setTranslation_select) {
|
||||
await Reading0.setTranslation_select(relatedTranslation_select0);
|
||||
}
|
||||
|
||||
const relatedTranslation_select1 = await Translations.findOne({
|
||||
offset: Math.floor(Math.random() * (await Translations.count())),
|
||||
});
|
||||
const Reading1 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 1,
|
||||
});
|
||||
if (Reading1?.setTranslation_select) {
|
||||
await Reading1.setTranslation_select(relatedTranslation_select1);
|
||||
}
|
||||
|
||||
const relatedTranslation_select2 = await Translations.findOne({
|
||||
offset: Math.floor(Math.random() * (await Translations.count())),
|
||||
});
|
||||
const Reading2 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 2,
|
||||
});
|
||||
if (Reading2?.setTranslation_select) {
|
||||
await Reading2.setTranslation_select(relatedTranslation_select2);
|
||||
}
|
||||
|
||||
const relatedTranslation_select3 = await Translations.findOne({
|
||||
offset: Math.floor(Math.random() * (await Translations.count())),
|
||||
});
|
||||
const Reading3 = await Readings.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 3,
|
||||
});
|
||||
if (Reading3?.setTranslation_select) {
|
||||
await Reading3.setTranslation_select(relatedTranslation_select3);
|
||||
}
|
||||
}
|
||||
|
||||
async function associateChapterWithBook() {
|
||||
const relatedBook0 = await Books.findOne({
|
||||
offset: Math.floor(Math.random() * (await Books.count())),
|
||||
});
|
||||
const Chapter0 = await Chapters.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 0,
|
||||
});
|
||||
if (Chapter0?.setBook) {
|
||||
await Chapter0.setBook(relatedBook0);
|
||||
}
|
||||
|
||||
const relatedBook1 = await Books.findOne({
|
||||
offset: Math.floor(Math.random() * (await Books.count())),
|
||||
});
|
||||
const Chapter1 = await Chapters.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 1,
|
||||
});
|
||||
if (Chapter1?.setBook) {
|
||||
await Chapter1.setBook(relatedBook1);
|
||||
}
|
||||
|
||||
const relatedBook2 = await Books.findOne({
|
||||
offset: Math.floor(Math.random() * (await Books.count())),
|
||||
});
|
||||
const Chapter2 = await Chapters.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 2,
|
||||
});
|
||||
if (Chapter2?.setBook) {
|
||||
await Chapter2.setBook(relatedBook2);
|
||||
}
|
||||
|
||||
const relatedBook3 = await Books.findOne({
|
||||
offset: Math.floor(Math.random() * (await Books.count())),
|
||||
});
|
||||
const Chapter3 = await Chapters.findOne({
|
||||
order: [['id', 'ASC']],
|
||||
offset: 3,
|
||||
});
|
||||
if (Chapter3?.setBook) {
|
||||
await Chapter3.setBook(relatedBook3);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@ -271,6 +837,14 @@ module.exports = {
|
||||
|
||||
await Readings.bulkCreate(ReadingsData);
|
||||
|
||||
await Books.bulkCreate(BooksData);
|
||||
|
||||
await Chapters.bulkCreate(ChaptersData);
|
||||
|
||||
await Verses.bulkCreate(VersesData);
|
||||
|
||||
await Translations.bulkCreate(TranslationsData);
|
||||
|
||||
await Promise.all([
|
||||
// Similar logic for "relation_many"
|
||||
|
||||
@ -281,6 +855,24 @@ module.exports = {
|
||||
await associateProgressWithUser(),
|
||||
|
||||
await associateReadingWithUser(),
|
||||
|
||||
await associateReadingWithBookref(),
|
||||
|
||||
await associateReadingWithChapterref(),
|
||||
|
||||
// Similar logic for "relation_many"
|
||||
|
||||
await associateReadingWithTranslationref(),
|
||||
|
||||
await associateReadingWithBook_select(),
|
||||
|
||||
await associateReadingWithChapter_select(),
|
||||
|
||||
// Similar logic for "relation_many"
|
||||
|
||||
await associateReadingWithTranslation_select(),
|
||||
|
||||
await associateChapterWithBook(),
|
||||
]);
|
||||
},
|
||||
|
||||
@ -290,5 +882,13 @@ module.exports = {
|
||||
await queryInterface.bulkDelete('progress', null, {});
|
||||
|
||||
await queryInterface.bulkDelete('readings', null, {});
|
||||
|
||||
await queryInterface.bulkDelete('books', null, {});
|
||||
|
||||
await queryInterface.bulkDelete('chapters', null, {});
|
||||
|
||||
await queryInterface.bulkDelete('verses', null, {});
|
||||
|
||||
await queryInterface.bulkDelete('translations', null, {});
|
||||
},
|
||||
};
|
||||
|
||||
@ -32,6 +32,9 @@ router.use(checkCrudPermissions('readings'));
|
||||
* notes:
|
||||
* type: string
|
||||
* default: notes
|
||||
* tags:
|
||||
* type: string
|
||||
* default: tags
|
||||
|
||||
* chapter:
|
||||
* type: integer
|
||||
@ -323,6 +326,7 @@ router.get(
|
||||
'verses',
|
||||
'translation',
|
||||
'notes',
|
||||
'tags',
|
||||
'chapter',
|
||||
|
||||
'date',
|
||||
|
||||
@ -47,12 +47,20 @@ module.exports = class SearchService {
|
||||
|
||||
progress: ['book'],
|
||||
|
||||
readings: ['book', 'verses', 'translation', 'notes'],
|
||||
readings: ['book', 'verses', 'translation', 'notes', 'tags'],
|
||||
|
||||
books: ['name'],
|
||||
|
||||
translations: ['name'],
|
||||
};
|
||||
const columnsInt = {
|
||||
progress: ['chapter'],
|
||||
|
||||
readings: ['chapter'],
|
||||
|
||||
chapters: ['number'],
|
||||
|
||||
verses: ['number'],
|
||||
};
|
||||
|
||||
let allFoundRecords = [];
|
||||
|
||||
@ -62,7 +62,7 @@ const CardReadings = ({
|
||||
href={`/readings/readings-view/?id=${item.id}`}
|
||||
className='text-lg font-bold leading-6 line-clamp-1'
|
||||
>
|
||||
{item.book}
|
||||
{item.tags}
|
||||
</Link>
|
||||
|
||||
<div className='ml-auto '>
|
||||
@ -140,6 +140,111 @@ const CardReadings = ({
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>
|
||||
Bookref
|
||||
</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
<div className='font-medium line-clamp-4'>
|
||||
{dataFormatter.booksOneListFormatter(item.bookref)}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>
|
||||
Chapterref
|
||||
</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
<div className='font-medium line-clamp-4'>
|
||||
{dataFormatter.chaptersOneListFormatter(item.chapterref)}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>
|
||||
Versesref
|
||||
</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
<div className='font-medium line-clamp-4'>
|
||||
{dataFormatter
|
||||
.versesManyListFormatter(item.versesref)
|
||||
.join(', ')}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>
|
||||
Translationref
|
||||
</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
<div className='font-medium line-clamp-4'>
|
||||
{dataFormatter.translationsOneListFormatter(
|
||||
item.translationref,
|
||||
)}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>
|
||||
Book_select
|
||||
</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
<div className='font-medium line-clamp-4'>
|
||||
{dataFormatter.booksOneListFormatter(item.book_select)}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>
|
||||
Chapter_select
|
||||
</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
<div className='font-medium line-clamp-4'>
|
||||
{dataFormatter.chaptersOneListFormatter(
|
||||
item.chapter_select,
|
||||
)}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>
|
||||
Verses_select
|
||||
</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
<div className='font-medium line-clamp-4'>
|
||||
{dataFormatter
|
||||
.versesManyListFormatter(item.verses_select)
|
||||
.join(', ')}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>
|
||||
Translation_select
|
||||
</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
<div className='font-medium line-clamp-4'>
|
||||
{dataFormatter.translationsOneListFormatter(
|
||||
item.translation_select,
|
||||
)}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>Tags</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
<div className='font-medium line-clamp-4'>{item.tags}</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@ -89,6 +89,87 @@ const ListReadings = ({
|
||||
{dataFormatter.usersOneListFormatter(item.user)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>Bookref</p>
|
||||
<p className={'line-clamp-2'}>
|
||||
{dataFormatter.booksOneListFormatter(item.bookref)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>Chapterref</p>
|
||||
<p className={'line-clamp-2'}>
|
||||
{dataFormatter.chaptersOneListFormatter(
|
||||
item.chapterref,
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>Versesref</p>
|
||||
<p className={'line-clamp-2'}>
|
||||
{dataFormatter
|
||||
.versesManyListFormatter(item.versesref)
|
||||
.join(', ')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>
|
||||
Translationref
|
||||
</p>
|
||||
<p className={'line-clamp-2'}>
|
||||
{dataFormatter.translationsOneListFormatter(
|
||||
item.translationref,
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>Book_select</p>
|
||||
<p className={'line-clamp-2'}>
|
||||
{dataFormatter.booksOneListFormatter(item.book_select)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>
|
||||
Chapter_select
|
||||
</p>
|
||||
<p className={'line-clamp-2'}>
|
||||
{dataFormatter.chaptersOneListFormatter(
|
||||
item.chapter_select,
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>
|
||||
Verses_select
|
||||
</p>
|
||||
<p className={'line-clamp-2'}>
|
||||
{dataFormatter
|
||||
.versesManyListFormatter(item.verses_select)
|
||||
.join(', ')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>
|
||||
Translation_select
|
||||
</p>
|
||||
<p className={'line-clamp-2'}>
|
||||
{dataFormatter.translationsOneListFormatter(
|
||||
item.translation_select,
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>Tags</p>
|
||||
<p className={'line-clamp-2'}>{item.tags}</p>
|
||||
</div>
|
||||
</Link>
|
||||
<ListActionsPopover
|
||||
onDelete={onDelete}
|
||||
|
||||
@ -135,6 +135,176 @@ export const loadColumns = async (
|
||||
params?.value?.id ?? params?.value,
|
||||
},
|
||||
|
||||
{
|
||||
field: 'bookref',
|
||||
headerName: 'Bookref',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
sortable: false,
|
||||
type: 'singleSelect',
|
||||
getOptionValue: (value: any) => value?.id,
|
||||
getOptionLabel: (value: any) => value?.label,
|
||||
valueOptions: await callOptionsApi('books'),
|
||||
valueGetter: (params: GridValueGetterParams) =>
|
||||
params?.value?.id ?? params?.value,
|
||||
},
|
||||
|
||||
{
|
||||
field: 'chapterref',
|
||||
headerName: 'Chapterref',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
sortable: false,
|
||||
type: 'singleSelect',
|
||||
getOptionValue: (value: any) => value?.id,
|
||||
getOptionLabel: (value: any) => value?.label,
|
||||
valueOptions: await callOptionsApi('chapters'),
|
||||
valueGetter: (params: GridValueGetterParams) =>
|
||||
params?.value?.id ?? params?.value,
|
||||
},
|
||||
|
||||
{
|
||||
field: 'versesref',
|
||||
headerName: 'Versesref',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
editable: false,
|
||||
sortable: false,
|
||||
type: 'singleSelect',
|
||||
valueFormatter: ({ value }) =>
|
||||
dataFormatter.versesManyListFormatter(value).join(', '),
|
||||
renderEditCell: (params) => (
|
||||
<DataGridMultiSelect {...params} entityName={'verses'} />
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
field: 'translationref',
|
||||
headerName: 'Translationref',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
sortable: false,
|
||||
type: 'singleSelect',
|
||||
getOptionValue: (value: any) => value?.id,
|
||||
getOptionLabel: (value: any) => value?.label,
|
||||
valueOptions: await callOptionsApi('translations'),
|
||||
valueGetter: (params: GridValueGetterParams) =>
|
||||
params?.value?.id ?? params?.value,
|
||||
},
|
||||
|
||||
{
|
||||
field: 'book_select',
|
||||
headerName: 'Book_select',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
sortable: false,
|
||||
type: 'singleSelect',
|
||||
getOptionValue: (value: any) => value?.id,
|
||||
getOptionLabel: (value: any) => value?.label,
|
||||
valueOptions: await callOptionsApi('books'),
|
||||
valueGetter: (params: GridValueGetterParams) =>
|
||||
params?.value?.id ?? params?.value,
|
||||
},
|
||||
|
||||
{
|
||||
field: 'chapter_select',
|
||||
headerName: 'Chapter_select',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
sortable: false,
|
||||
type: 'singleSelect',
|
||||
getOptionValue: (value: any) => value?.id,
|
||||
getOptionLabel: (value: any) => value?.label,
|
||||
valueOptions: await callOptionsApi('chapters'),
|
||||
valueGetter: (params: GridValueGetterParams) =>
|
||||
params?.value?.id ?? params?.value,
|
||||
},
|
||||
|
||||
{
|
||||
field: 'verses_select',
|
||||
headerName: 'Verses_select',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
editable: false,
|
||||
sortable: false,
|
||||
type: 'singleSelect',
|
||||
valueFormatter: ({ value }) =>
|
||||
dataFormatter.versesManyListFormatter(value).join(', '),
|
||||
renderEditCell: (params) => (
|
||||
<DataGridMultiSelect {...params} entityName={'verses'} />
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
field: 'translation_select',
|
||||
headerName: 'Translation_select',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
sortable: false,
|
||||
type: 'singleSelect',
|
||||
getOptionValue: (value: any) => value?.id,
|
||||
getOptionLabel: (value: any) => value?.label,
|
||||
valueOptions: await callOptionsApi('translations'),
|
||||
valueGetter: (params: GridValueGetterParams) =>
|
||||
params?.value?.id ?? params?.value,
|
||||
},
|
||||
|
||||
{
|
||||
field: 'tags',
|
||||
headerName: 'Tags',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
},
|
||||
|
||||
{
|
||||
field: 'actions',
|
||||
type: 'actions',
|
||||
|
||||
@ -17,9 +17,9 @@ export default function WebSiteFooter({ projectName }: WebSiteFooterProps) {
|
||||
const borders = useAppSelector((state) => state.style.borders);
|
||||
const websiteHeder = useAppSelector((state) => state.style.websiteHeder);
|
||||
|
||||
const style = FooterStyle.WITH_PROJECT_NAME;
|
||||
const style = FooterStyle.WITH_PAGES;
|
||||
|
||||
const design = FooterDesigns.DEFAULT_DESIGN;
|
||||
const design = FooterDesigns.DESIGN_DIVERSITY;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@ -17,9 +17,9 @@ export default function WebSiteHeader({ projectName }: WebSiteHeaderProps) {
|
||||
const websiteHeder = useAppSelector((state) => state.style.websiteHeder);
|
||||
const borders = useAppSelector((state) => state.style.borders);
|
||||
|
||||
const style = HeaderStyle.PAGES_RIGHT;
|
||||
const style = HeaderStyle.PAGES_LEFT;
|
||||
|
||||
const design = HeaderDesigns.DESIGN_DIVERSITY;
|
||||
const design = HeaderDesigns.DEFAULT_DESIGN;
|
||||
return (
|
||||
<header id='websiteHeader' className='overflow-hidden'>
|
||||
<div
|
||||
|
||||
@ -60,21 +60,21 @@ export default {
|
||||
|
||||
readingsManyListFormatter(val) {
|
||||
if (!val || !val.length) return [];
|
||||
return val.map((item) => item.book);
|
||||
return val.map((item) => item.tags);
|
||||
},
|
||||
readingsOneListFormatter(val) {
|
||||
if (!val) return '';
|
||||
return val.book;
|
||||
return val.tags;
|
||||
},
|
||||
readingsManyListFormatterEdit(val) {
|
||||
if (!val || !val.length) return [];
|
||||
return val.map((item) => {
|
||||
return { id: item.id, label: item.book };
|
||||
return { id: item.id, label: item.tags };
|
||||
});
|
||||
},
|
||||
readingsOneListFormatterEdit(val) {
|
||||
if (!val) return '';
|
||||
return { label: val.book, id: val.id };
|
||||
return { label: val.tags, id: val.id };
|
||||
},
|
||||
|
||||
rolesManyListFormatter(val) {
|
||||
@ -114,4 +114,80 @@ export default {
|
||||
if (!val) return '';
|
||||
return { label: val.name, id: val.id };
|
||||
},
|
||||
|
||||
booksManyListFormatter(val) {
|
||||
if (!val || !val.length) return [];
|
||||
return val.map((item) => item.name);
|
||||
},
|
||||
booksOneListFormatter(val) {
|
||||
if (!val) return '';
|
||||
return val.name;
|
||||
},
|
||||
booksManyListFormatterEdit(val) {
|
||||
if (!val || !val.length) return [];
|
||||
return val.map((item) => {
|
||||
return { id: item.id, label: item.name };
|
||||
});
|
||||
},
|
||||
booksOneListFormatterEdit(val) {
|
||||
if (!val) return '';
|
||||
return { label: val.name, id: val.id };
|
||||
},
|
||||
|
||||
chaptersManyListFormatter(val) {
|
||||
if (!val || !val.length) return [];
|
||||
return val.map((item) => item.number);
|
||||
},
|
||||
chaptersOneListFormatter(val) {
|
||||
if (!val) return '';
|
||||
return val.number;
|
||||
},
|
||||
chaptersManyListFormatterEdit(val) {
|
||||
if (!val || !val.length) return [];
|
||||
return val.map((item) => {
|
||||
return { id: item.id, label: item.number };
|
||||
});
|
||||
},
|
||||
chaptersOneListFormatterEdit(val) {
|
||||
if (!val) return '';
|
||||
return { label: val.number, id: val.id };
|
||||
},
|
||||
|
||||
versesManyListFormatter(val) {
|
||||
if (!val || !val.length) return [];
|
||||
return val.map((item) => item.number);
|
||||
},
|
||||
versesOneListFormatter(val) {
|
||||
if (!val) return '';
|
||||
return val.number;
|
||||
},
|
||||
versesManyListFormatterEdit(val) {
|
||||
if (!val || !val.length) return [];
|
||||
return val.map((item) => {
|
||||
return { id: item.id, label: item.number };
|
||||
});
|
||||
},
|
||||
versesOneListFormatterEdit(val) {
|
||||
if (!val) return '';
|
||||
return { label: val.number, id: val.id };
|
||||
},
|
||||
|
||||
translationsManyListFormatter(val) {
|
||||
if (!val || !val.length) return [];
|
||||
return val.map((item) => item.name);
|
||||
},
|
||||
translationsOneListFormatter(val) {
|
||||
if (!val) return '';
|
||||
return val.name;
|
||||
},
|
||||
translationsManyListFormatterEdit(val) {
|
||||
if (!val || !val.length) return [];
|
||||
return val.map((item) => {
|
||||
return { id: item.id, label: item.name };
|
||||
});
|
||||
},
|
||||
translationsOneListFormatterEdit(val) {
|
||||
if (!val) return '';
|
||||
return { label: val.name, id: val.id };
|
||||
},
|
||||
};
|
||||
|
||||
236
frontend/src/pages/books/books-view.tsx
Normal file
236
frontend/src/pages/books/books-view.tsx
Normal file
@ -0,0 +1,236 @@
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import dayjs from 'dayjs';
|
||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks';
|
||||
import { useRouter } from 'next/router';
|
||||
import { fetch } from '../../stores/books/booksSlice';
|
||||
import { saveFile } from '../../helpers/fileSaver';
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from '../../components/ImageField';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { getPageTitle } from '../../config';
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton';
|
||||
import SectionMain from '../../components/SectionMain';
|
||||
import CardBox from '../../components/CardBox';
|
||||
import BaseButton from '../../components/BaseButton';
|
||||
import BaseDivider from '../../components/BaseDivider';
|
||||
import { mdiChartTimelineVariant } from '@mdi/js';
|
||||
import { SwitchField } from '../../components/SwitchField';
|
||||
import FormField from '../../components/FormField';
|
||||
|
||||
const BooksView = () => {
|
||||
const router = useRouter();
|
||||
const dispatch = useAppDispatch();
|
||||
const { books } = useAppSelector((state) => state.books);
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str, `str`);
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View books')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton
|
||||
icon={mdiChartTimelineVariant}
|
||||
title={removeLastCharacter('View books')}
|
||||
main
|
||||
>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/books/books-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Name</p>
|
||||
<p>{books?.name}</p>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Readings Bookref</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
|
||||
<th>Book</th>
|
||||
|
||||
<th>Chapter</th>
|
||||
|
||||
<th>Verses</th>
|
||||
|
||||
<th>Translation</th>
|
||||
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{books.readings_bookref &&
|
||||
Array.isArray(books.readings_bookref) &&
|
||||
books.readings_bookref.map((item: any) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/readings/readings-view/?id=${item.id}`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<td data-label='date'>
|
||||
{dataFormatter.dateTimeFormatter(item.date)}
|
||||
</td>
|
||||
|
||||
<td data-label='book'>{item.book}</td>
|
||||
|
||||
<td data-label='chapter'>{item.chapter}</td>
|
||||
|
||||
<td data-label='verses'>{item.verses}</td>
|
||||
|
||||
<td data-label='translation'>{item.translation}</td>
|
||||
|
||||
<td data-label='tags'>{item.tags}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!books?.readings_bookref?.length && (
|
||||
<div className={'text-center py-4'}>No data</div>
|
||||
)}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Readings Book_select</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
|
||||
<th>Book</th>
|
||||
|
||||
<th>Chapter</th>
|
||||
|
||||
<th>Verses</th>
|
||||
|
||||
<th>Translation</th>
|
||||
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{books.readings_book_select &&
|
||||
Array.isArray(books.readings_book_select) &&
|
||||
books.readings_book_select.map((item: any) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/readings/readings-view/?id=${item.id}`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<td data-label='date'>
|
||||
{dataFormatter.dateTimeFormatter(item.date)}
|
||||
</td>
|
||||
|
||||
<td data-label='book'>{item.book}</td>
|
||||
|
||||
<td data-label='chapter'>{item.chapter}</td>
|
||||
|
||||
<td data-label='verses'>{item.verses}</td>
|
||||
|
||||
<td data-label='translation'>{item.translation}</td>
|
||||
|
||||
<td data-label='tags'>{item.tags}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!books?.readings_book_select?.length && (
|
||||
<div className={'text-center py-4'}>No data</div>
|
||||
)}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Chapters Book</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Number</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{books.chapters_book &&
|
||||
Array.isArray(books.chapters_book) &&
|
||||
books.chapters_book.map((item: any) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/chapters/chapters-view/?id=${item.id}`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<td data-label='number'>{item.number}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!books?.chapters_book?.length && (
|
||||
<div className={'text-center py-4'}>No data</div>
|
||||
)}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/books/books-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
BooksView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated permission={'READ_BOOKS'}>{page}</LayoutAuthenticated>
|
||||
);
|
||||
};
|
||||
|
||||
export default BooksView;
|
||||
207
frontend/src/pages/chapters/chapters-view.tsx
Normal file
207
frontend/src/pages/chapters/chapters-view.tsx
Normal file
@ -0,0 +1,207 @@
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import dayjs from 'dayjs';
|
||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks';
|
||||
import { useRouter } from 'next/router';
|
||||
import { fetch } from '../../stores/chapters/chaptersSlice';
|
||||
import { saveFile } from '../../helpers/fileSaver';
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from '../../components/ImageField';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { getPageTitle } from '../../config';
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton';
|
||||
import SectionMain from '../../components/SectionMain';
|
||||
import CardBox from '../../components/CardBox';
|
||||
import BaseButton from '../../components/BaseButton';
|
||||
import BaseDivider from '../../components/BaseDivider';
|
||||
import { mdiChartTimelineVariant } from '@mdi/js';
|
||||
import { SwitchField } from '../../components/SwitchField';
|
||||
import FormField from '../../components/FormField';
|
||||
|
||||
const ChaptersView = () => {
|
||||
const router = useRouter();
|
||||
const dispatch = useAppDispatch();
|
||||
const { chapters } = useAppSelector((state) => state.chapters);
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str, `str`);
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View chapters')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton
|
||||
icon={mdiChartTimelineVariant}
|
||||
title={removeLastCharacter('View chapters')}
|
||||
main
|
||||
>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/chapters/chapters-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Number</p>
|
||||
<p>{chapters?.number || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Book</p>
|
||||
|
||||
<p>{chapters?.book?.name ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Readings Chapterref</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
|
||||
<th>Book</th>
|
||||
|
||||
<th>Chapter</th>
|
||||
|
||||
<th>Verses</th>
|
||||
|
||||
<th>Translation</th>
|
||||
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{chapters.readings_chapterref &&
|
||||
Array.isArray(chapters.readings_chapterref) &&
|
||||
chapters.readings_chapterref.map((item: any) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/readings/readings-view/?id=${item.id}`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<td data-label='date'>
|
||||
{dataFormatter.dateTimeFormatter(item.date)}
|
||||
</td>
|
||||
|
||||
<td data-label='book'>{item.book}</td>
|
||||
|
||||
<td data-label='chapter'>{item.chapter}</td>
|
||||
|
||||
<td data-label='verses'>{item.verses}</td>
|
||||
|
||||
<td data-label='translation'>{item.translation}</td>
|
||||
|
||||
<td data-label='tags'>{item.tags}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!chapters?.readings_chapterref?.length && (
|
||||
<div className={'text-center py-4'}>No data</div>
|
||||
)}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Readings Chapter_select</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
|
||||
<th>Book</th>
|
||||
|
||||
<th>Chapter</th>
|
||||
|
||||
<th>Verses</th>
|
||||
|
||||
<th>Translation</th>
|
||||
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{chapters.readings_chapter_select &&
|
||||
Array.isArray(chapters.readings_chapter_select) &&
|
||||
chapters.readings_chapter_select.map((item: any) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/readings/readings-view/?id=${item.id}`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<td data-label='date'>
|
||||
{dataFormatter.dateTimeFormatter(item.date)}
|
||||
</td>
|
||||
|
||||
<td data-label='book'>{item.book}</td>
|
||||
|
||||
<td data-label='chapter'>{item.chapter}</td>
|
||||
|
||||
<td data-label='verses'>{item.verses}</td>
|
||||
|
||||
<td data-label='translation'>{item.translation}</td>
|
||||
|
||||
<td data-label='tags'>{item.tags}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!chapters?.readings_chapter_select?.length && (
|
||||
<div className={'text-center py-4'}>No data</div>
|
||||
)}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/chapters/chapters-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ChaptersView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated permission={'READ_CHAPTERS'}>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChaptersView;
|
||||
@ -131,7 +131,7 @@ export default function WebSite() {
|
||||
<FeaturesSection
|
||||
projectName={'Bible Reading Tracker'}
|
||||
image={['Icons representing app features']}
|
||||
withBg={0}
|
||||
withBg={1}
|
||||
features={features_points}
|
||||
mainText={`Explore ${projectName} Features`}
|
||||
subTitle={`Discover the powerful features of ${projectName} designed to enhance your Bible reading experience.`}
|
||||
|
||||
@ -114,7 +114,7 @@ const EditNotes = () => {
|
||||
component={SelectField}
|
||||
options={initialValues.reading}
|
||||
itemRef={'readings'}
|
||||
showField={'book'}
|
||||
showField={'tags'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
|
||||
@ -112,7 +112,7 @@ const EditNotesPage = () => {
|
||||
component={SelectField}
|
||||
options={initialValues.reading}
|
||||
itemRef={'readings'}
|
||||
showField={'book'}
|
||||
showField={'tags'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
|
||||
@ -71,7 +71,7 @@ const NotesView = () => {
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Reading</p>
|
||||
|
||||
<p>{notes?.reading?.book ?? 'No data'}</p>
|
||||
<p>{notes?.reading?.tags ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
<div className={'mb-4'}>
|
||||
|
||||
@ -49,6 +49,24 @@ const EditReadings = () => {
|
||||
notes: '',
|
||||
|
||||
user: null,
|
||||
|
||||
bookref: null,
|
||||
|
||||
chapterref: null,
|
||||
|
||||
versesref: [],
|
||||
|
||||
translationref: null,
|
||||
|
||||
book_select: null,
|
||||
|
||||
chapter_select: null,
|
||||
|
||||
verses_select: [],
|
||||
|
||||
translation_select: null,
|
||||
|
||||
tags: '',
|
||||
};
|
||||
const [initialValues, setInitialValues] = useState(initVals);
|
||||
|
||||
@ -153,6 +171,101 @@ const EditReadings = () => {
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Bookref' labelFor='bookref'>
|
||||
<Field
|
||||
name='bookref'
|
||||
id='bookref'
|
||||
component={SelectField}
|
||||
options={initialValues.bookref}
|
||||
itemRef={'books'}
|
||||
showField={'name'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Chapterref' labelFor='chapterref'>
|
||||
<Field
|
||||
name='chapterref'
|
||||
id='chapterref'
|
||||
component={SelectField}
|
||||
options={initialValues.chapterref}
|
||||
itemRef={'chapters'}
|
||||
showField={'number'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Versesref' labelFor='versesref'>
|
||||
<Field
|
||||
name='versesref'
|
||||
id='versesref'
|
||||
component={SelectFieldMany}
|
||||
options={initialValues.versesref}
|
||||
itemRef={'verses'}
|
||||
showField={'number'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Translationref' labelFor='translationref'>
|
||||
<Field
|
||||
name='translationref'
|
||||
id='translationref'
|
||||
component={SelectField}
|
||||
options={initialValues.translationref}
|
||||
itemRef={'translations'}
|
||||
showField={'name'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Book_select' labelFor='book_select'>
|
||||
<Field
|
||||
name='book_select'
|
||||
id='book_select'
|
||||
component={SelectField}
|
||||
options={initialValues.book_select}
|
||||
itemRef={'books'}
|
||||
showField={'name'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Chapter_select' labelFor='chapter_select'>
|
||||
<Field
|
||||
name='chapter_select'
|
||||
id='chapter_select'
|
||||
component={SelectField}
|
||||
options={initialValues.chapter_select}
|
||||
itemRef={'chapters'}
|
||||
showField={'number'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Verses_select' labelFor='verses_select'>
|
||||
<Field
|
||||
name='verses_select'
|
||||
id='verses_select'
|
||||
component={SelectFieldMany}
|
||||
options={initialValues.verses_select}
|
||||
itemRef={'verses'}
|
||||
showField={'number'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField
|
||||
label='Translation_select'
|
||||
labelFor='translation_select'
|
||||
>
|
||||
<Field
|
||||
name='translation_select'
|
||||
id='translation_select'
|
||||
component={SelectField}
|
||||
options={initialValues.translation_select}
|
||||
itemRef={'translations'}
|
||||
showField={'name'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Tags'>
|
||||
<Field name='tags' placeholder='Tags' />
|
||||
</FormField>
|
||||
|
||||
<BaseDivider />
|
||||
<BaseButtons>
|
||||
<BaseButton type='submit' color='info' label='Submit' />
|
||||
|
||||
@ -49,6 +49,24 @@ const EditReadingsPage = () => {
|
||||
notes: '',
|
||||
|
||||
user: null,
|
||||
|
||||
bookref: null,
|
||||
|
||||
chapterref: null,
|
||||
|
||||
versesref: [],
|
||||
|
||||
translationref: null,
|
||||
|
||||
book_select: null,
|
||||
|
||||
chapter_select: null,
|
||||
|
||||
verses_select: [],
|
||||
|
||||
translation_select: null,
|
||||
|
||||
tags: '',
|
||||
};
|
||||
const [initialValues, setInitialValues] = useState(initVals);
|
||||
|
||||
@ -151,6 +169,101 @@ const EditReadingsPage = () => {
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Bookref' labelFor='bookref'>
|
||||
<Field
|
||||
name='bookref'
|
||||
id='bookref'
|
||||
component={SelectField}
|
||||
options={initialValues.bookref}
|
||||
itemRef={'books'}
|
||||
showField={'name'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Chapterref' labelFor='chapterref'>
|
||||
<Field
|
||||
name='chapterref'
|
||||
id='chapterref'
|
||||
component={SelectField}
|
||||
options={initialValues.chapterref}
|
||||
itemRef={'chapters'}
|
||||
showField={'number'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Versesref' labelFor='versesref'>
|
||||
<Field
|
||||
name='versesref'
|
||||
id='versesref'
|
||||
component={SelectFieldMany}
|
||||
options={initialValues.versesref}
|
||||
itemRef={'verses'}
|
||||
showField={'number'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Translationref' labelFor='translationref'>
|
||||
<Field
|
||||
name='translationref'
|
||||
id='translationref'
|
||||
component={SelectField}
|
||||
options={initialValues.translationref}
|
||||
itemRef={'translations'}
|
||||
showField={'name'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Book_select' labelFor='book_select'>
|
||||
<Field
|
||||
name='book_select'
|
||||
id='book_select'
|
||||
component={SelectField}
|
||||
options={initialValues.book_select}
|
||||
itemRef={'books'}
|
||||
showField={'name'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Chapter_select' labelFor='chapter_select'>
|
||||
<Field
|
||||
name='chapter_select'
|
||||
id='chapter_select'
|
||||
component={SelectField}
|
||||
options={initialValues.chapter_select}
|
||||
itemRef={'chapters'}
|
||||
showField={'number'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Verses_select' labelFor='verses_select'>
|
||||
<Field
|
||||
name='verses_select'
|
||||
id='verses_select'
|
||||
component={SelectFieldMany}
|
||||
options={initialValues.verses_select}
|
||||
itemRef={'verses'}
|
||||
showField={'number'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField
|
||||
label='Translation_select'
|
||||
labelFor='translation_select'
|
||||
>
|
||||
<Field
|
||||
name='translation_select'
|
||||
id='translation_select'
|
||||
component={SelectField}
|
||||
options={initialValues.translation_select}
|
||||
itemRef={'translations'}
|
||||
showField={'name'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Tags'>
|
||||
<Field name='tags' placeholder='Tags' />
|
||||
</FormField>
|
||||
|
||||
<BaseDivider />
|
||||
<BaseButtons>
|
||||
<BaseButton type='submit' color='info' label='Submit' />
|
||||
|
||||
@ -33,11 +33,27 @@ const ReadingsTablesPage = () => {
|
||||
{ label: 'Verses', title: 'verses' },
|
||||
{ label: 'Translation', title: 'translation' },
|
||||
{ label: 'Notes', title: 'notes' },
|
||||
{ label: 'Tags', title: 'tags' },
|
||||
{ label: 'Chapter', title: 'chapter', number: 'true' },
|
||||
|
||||
{ label: 'Date', title: 'date', date: 'true' },
|
||||
|
||||
{ label: 'User', title: 'user' },
|
||||
|
||||
{ label: 'Bookref', title: 'bookref' },
|
||||
|
||||
{ label: 'Chapterref', title: 'chapterref' },
|
||||
|
||||
{ label: 'Translationref', title: 'translationref' },
|
||||
|
||||
{ label: 'Book_select', title: 'book_select' },
|
||||
|
||||
{ label: 'Chapter_select', title: 'chapter_select' },
|
||||
|
||||
{ label: 'Translation_select', title: 'translation_select' },
|
||||
|
||||
{ label: 'Versesref', title: 'versesref' },
|
||||
{ label: 'Verses_select', title: 'verses_select' },
|
||||
]);
|
||||
|
||||
const hasCreatePermission =
|
||||
|
||||
@ -46,6 +46,24 @@ const initialValues = {
|
||||
notes: '',
|
||||
|
||||
user: '',
|
||||
|
||||
bookref: '',
|
||||
|
||||
chapterref: '',
|
||||
|
||||
versesref: [],
|
||||
|
||||
translationref: '',
|
||||
|
||||
book_select: '',
|
||||
|
||||
chapter_select: '',
|
||||
|
||||
verses_select: [],
|
||||
|
||||
translation_select: '',
|
||||
|
||||
tags: '',
|
||||
};
|
||||
|
||||
const ReadingsNew = () => {
|
||||
@ -113,6 +131,93 @@ const ReadingsNew = () => {
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Bookref' labelFor='bookref'>
|
||||
<Field
|
||||
name='bookref'
|
||||
id='bookref'
|
||||
component={SelectField}
|
||||
options={[]}
|
||||
itemRef={'books'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Chapterref' labelFor='chapterref'>
|
||||
<Field
|
||||
name='chapterref'
|
||||
id='chapterref'
|
||||
component={SelectField}
|
||||
options={[]}
|
||||
itemRef={'chapters'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Versesref' labelFor='versesref'>
|
||||
<Field
|
||||
name='versesref'
|
||||
id='versesref'
|
||||
itemRef={'verses'}
|
||||
options={[]}
|
||||
component={SelectFieldMany}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Translationref' labelFor='translationref'>
|
||||
<Field
|
||||
name='translationref'
|
||||
id='translationref'
|
||||
component={SelectField}
|
||||
options={[]}
|
||||
itemRef={'translations'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Book_select' labelFor='book_select'>
|
||||
<Field
|
||||
name='book_select'
|
||||
id='book_select'
|
||||
component={SelectField}
|
||||
options={[]}
|
||||
itemRef={'books'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Chapter_select' labelFor='chapter_select'>
|
||||
<Field
|
||||
name='chapter_select'
|
||||
id='chapter_select'
|
||||
component={SelectField}
|
||||
options={[]}
|
||||
itemRef={'chapters'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Verses_select' labelFor='verses_select'>
|
||||
<Field
|
||||
name='verses_select'
|
||||
id='verses_select'
|
||||
itemRef={'verses'}
|
||||
options={[]}
|
||||
component={SelectFieldMany}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField
|
||||
label='Translation_select'
|
||||
labelFor='translation_select'
|
||||
>
|
||||
<Field
|
||||
name='translation_select'
|
||||
id='translation_select'
|
||||
component={SelectField}
|
||||
options={[]}
|
||||
itemRef={'translations'}
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Tags'>
|
||||
<Field name='tags' placeholder='Tags' />
|
||||
</FormField>
|
||||
|
||||
<BaseDivider />
|
||||
<BaseButtons>
|
||||
<BaseButton type='submit' color='info' label='Submit' />
|
||||
|
||||
@ -33,11 +33,27 @@ const ReadingsTablesPage = () => {
|
||||
{ label: 'Verses', title: 'verses' },
|
||||
{ label: 'Translation', title: 'translation' },
|
||||
{ label: 'Notes', title: 'notes' },
|
||||
{ label: 'Tags', title: 'tags' },
|
||||
{ label: 'Chapter', title: 'chapter', number: 'true' },
|
||||
|
||||
{ label: 'Date', title: 'date', date: 'true' },
|
||||
|
||||
{ label: 'User', title: 'user' },
|
||||
|
||||
{ label: 'Bookref', title: 'bookref' },
|
||||
|
||||
{ label: 'Chapterref', title: 'chapterref' },
|
||||
|
||||
{ label: 'Translationref', title: 'translationref' },
|
||||
|
||||
{ label: 'Book_select', title: 'book_select' },
|
||||
|
||||
{ label: 'Chapter_select', title: 'chapter_select' },
|
||||
|
||||
{ label: 'Translation_select', title: 'translation_select' },
|
||||
|
||||
{ label: 'Versesref', title: 'versesref' },
|
||||
{ label: 'Verses_select', title: 'verses_select' },
|
||||
]);
|
||||
|
||||
const hasCreatePermission =
|
||||
|
||||
@ -106,6 +106,117 @@ const ReadingsView = () => {
|
||||
<p>{readings?.user?.firstName ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Bookref</p>
|
||||
|
||||
<p>{readings?.bookref?.name ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Chapterref</p>
|
||||
|
||||
<p>{readings?.chapterref?.number ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Versesref</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Number</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{readings.versesref &&
|
||||
Array.isArray(readings.versesref) &&
|
||||
readings.versesref.map((item: any) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
onClick={() =>
|
||||
router.push(`/verses/verses-view/?id=${item.id}`)
|
||||
}
|
||||
>
|
||||
<td data-label='number'>{item.number}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!readings?.versesref?.length && (
|
||||
<div className={'text-center py-4'}>No data</div>
|
||||
)}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Translationref</p>
|
||||
|
||||
<p>{readings?.translationref?.name ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Book_select</p>
|
||||
|
||||
<p>{readings?.book_select?.name ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Chapter_select</p>
|
||||
|
||||
<p>{readings?.chapter_select?.number ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Verses_select</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Number</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{readings.verses_select &&
|
||||
Array.isArray(readings.verses_select) &&
|
||||
readings.verses_select.map((item: any) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
onClick={() =>
|
||||
router.push(`/verses/verses-view/?id=${item.id}`)
|
||||
}
|
||||
>
|
||||
<td data-label='number'>{item.number}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!readings?.verses_select?.length && (
|
||||
<div className={'text-center py-4'}>No data</div>
|
||||
)}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Translation_select</p>
|
||||
|
||||
<p>{readings?.translation_select?.name ?? 'No data'}</p>
|
||||
</div>
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Tags</p>
|
||||
<p>{readings?.tags}</p>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Notes Reading</p>
|
||||
<CardBox
|
||||
|
||||
205
frontend/src/pages/translations/translations-view.tsx
Normal file
205
frontend/src/pages/translations/translations-view.tsx
Normal file
@ -0,0 +1,205 @@
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import dayjs from 'dayjs';
|
||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks';
|
||||
import { useRouter } from 'next/router';
|
||||
import { fetch } from '../../stores/translations/translationsSlice';
|
||||
import { saveFile } from '../../helpers/fileSaver';
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from '../../components/ImageField';
|
||||
import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import { getPageTitle } from '../../config';
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton';
|
||||
import SectionMain from '../../components/SectionMain';
|
||||
import CardBox from '../../components/CardBox';
|
||||
import BaseButton from '../../components/BaseButton';
|
||||
import BaseDivider from '../../components/BaseDivider';
|
||||
import { mdiChartTimelineVariant } from '@mdi/js';
|
||||
import { SwitchField } from '../../components/SwitchField';
|
||||
import FormField from '../../components/FormField';
|
||||
|
||||
const TranslationsView = () => {
|
||||
const router = useRouter();
|
||||
const dispatch = useAppDispatch();
|
||||
const { translations } = useAppSelector((state) => state.translations);
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str, `str`);
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View translations')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton
|
||||
icon={mdiChartTimelineVariant}
|
||||
title={removeLastCharacter('View translations')}
|
||||
main
|
||||
>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/translations/translations-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Name</p>
|
||||
<p>{translations?.name}</p>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Readings Translationref</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
|
||||
<th>Book</th>
|
||||
|
||||
<th>Chapter</th>
|
||||
|
||||
<th>Verses</th>
|
||||
|
||||
<th>Translation</th>
|
||||
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{translations.readings_translationref &&
|
||||
Array.isArray(translations.readings_translationref) &&
|
||||
translations.readings_translationref.map((item: any) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/readings/readings-view/?id=${item.id}`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<td data-label='date'>
|
||||
{dataFormatter.dateTimeFormatter(item.date)}
|
||||
</td>
|
||||
|
||||
<td data-label='book'>{item.book}</td>
|
||||
|
||||
<td data-label='chapter'>{item.chapter}</td>
|
||||
|
||||
<td data-label='verses'>{item.verses}</td>
|
||||
|
||||
<td data-label='translation'>{item.translation}</td>
|
||||
|
||||
<td data-label='tags'>{item.tags}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!translations?.readings_translationref?.length && (
|
||||
<div className={'text-center py-4'}>No data</div>
|
||||
)}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>
|
||||
Readings Translation_select
|
||||
</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
|
||||
<th>Book</th>
|
||||
|
||||
<th>Chapter</th>
|
||||
|
||||
<th>Verses</th>
|
||||
|
||||
<th>Translation</th>
|
||||
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{translations.readings_translation_select &&
|
||||
Array.isArray(translations.readings_translation_select) &&
|
||||
translations.readings_translation_select.map(
|
||||
(item: any) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/readings/readings-view/?id=${item.id}`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<td data-label='date'>
|
||||
{dataFormatter.dateTimeFormatter(item.date)}
|
||||
</td>
|
||||
|
||||
<td data-label='book'>{item.book}</td>
|
||||
|
||||
<td data-label='chapter'>{item.chapter}</td>
|
||||
|
||||
<td data-label='verses'>{item.verses}</td>
|
||||
|
||||
<td data-label='translation'>{item.translation}</td>
|
||||
|
||||
<td data-label='tags'>{item.tags}</td>
|
||||
</tr>
|
||||
),
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!translations?.readings_translation_select?.length && (
|
||||
<div className={'text-center py-4'}>No data</div>
|
||||
)}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/translations/translations-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
TranslationsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated permission={'READ_TRANSLATIONS'}>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
);
|
||||
};
|
||||
|
||||
export default TranslationsView;
|
||||
@ -239,6 +239,8 @@ const UsersView = () => {
|
||||
<th>Verses</th>
|
||||
|
||||
<th>Translation</th>
|
||||
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -264,6 +266,8 @@ const UsersView = () => {
|
||||
<td data-label='verses'>{item.verses}</td>
|
||||
|
||||
<td data-label='translation'>{item.translation}</td>
|
||||
|
||||
<td data-label='tags'>{item.tags}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user