Auto commit: 2025-09-18T16:05:59.956Z

This commit is contained in:
Flatlogic Bot 2025-09-18 16:05:59 +00:00
parent f0ac3b80cd
commit 9658341031
38 changed files with 3937 additions and 20 deletions

File diff suppressed because one or more lines are too long

258
backend/src/db/api/books.js Normal file
View 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,
}));
}
};

View 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,
}));
}
};

View File

@ -191,7 +191,7 @@ module.exports = class NotesDBApi {
},
},
{
book: {
tags: {
[Op.or]: filter.reading
.split('|')
.map((term) => ({ [Op.iLike]: `%${term}%` })),

View File

@ -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,
}));
}
};

View 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,
}));
}
};

View 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;
}
},
};

View 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;
}
},
};

View 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;
}
},
};

View 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;
}
},
};

View 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;
}
},
};

View 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;
};

View 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;
};

View File

@ -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',
});

View 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;
};

View File

@ -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, {});
},
};

View File

@ -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',

View File

@ -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 = [];

View File

@ -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>
))}

View File

@ -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}

View File

@ -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',

View File

@ -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

View File

@ -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

View File

@ -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 };
},
};

View 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;

View 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;

View File

@ -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.`}

View File

@ -114,7 +114,7 @@ const EditNotes = () => {
component={SelectField}
options={initialValues.reading}
itemRef={'readings'}
showField={'book'}
showField={'tags'}
></Field>
</FormField>

View File

@ -112,7 +112,7 @@ const EditNotesPage = () => {
component={SelectField}
options={initialValues.reading}
itemRef={'readings'}
showField={'book'}
showField={'tags'}
></Field>
</FormField>

View File

@ -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'}>

View File

@ -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' />

View File

@ -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' />

View File

@ -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 =

View File

@ -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' />

View File

@ -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 =

View File

@ -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

View 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;

View File

@ -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>