From dce1a42c612d0a89b7d82966e568a38cdd46c921 Mon Sep 17 00:00:00 2001 From: James Chang Date: Sat, 22 May 2021 17:50:21 -0400 Subject: [PATCH] Resolve #20: Add support for sub-events --- .../20210519215333_add_event_is_subevent.ts | 13 +++ .../20210520213254_add_event_is_wca_event.ts | 13 +++ backend/functions/migration.ts | 102 ++++++++++++++++-- backend/functions/schema.ts | 6 ++ .../src/schema/models/event/typeDef.ts | 15 +++ .../components/table/common/eventColumn.vue | 24 ++++- frontend/models/event.ts | 42 +++++++- frontend/models/personalBest.ts | 6 +- frontend/types/schema.ts | 6 ++ 9 files changed, 209 insertions(+), 18 deletions(-) create mode 100644 backend/functions/db/migrations/20210519215333_add_event_is_subevent.ts create mode 100644 backend/functions/db/migrations/20210520213254_add_event_is_wca_event.ts diff --git a/backend/functions/db/migrations/20210519215333_add_event_is_subevent.ts b/backend/functions/db/migrations/20210519215333_add_event_is_subevent.ts new file mode 100644 index 0000000..1916aeb --- /dev/null +++ b/backend/functions/db/migrations/20210519215333_add_event_is_subevent.ts @@ -0,0 +1,13 @@ +import * as Knex from "knex"; + +export async function up(knex: Knex): Promise { + return knex.schema.alterTable("event", function (t) { + t.boolean("is_sub_event").notNullable().defaultTo(false); + }); +} + +export async function down(knex: Knex): Promise { + return knex.schema.alterTable("event", function (t) { + t.dropColumn("is_sub_event"); + }); +} diff --git a/backend/functions/db/migrations/20210520213254_add_event_is_wca_event.ts b/backend/functions/db/migrations/20210520213254_add_event_is_wca_event.ts new file mode 100644 index 0000000..f6785d2 --- /dev/null +++ b/backend/functions/db/migrations/20210520213254_add_event_is_wca_event.ts @@ -0,0 +1,13 @@ +import * as Knex from "knex"; + +export async function up(knex: Knex): Promise { + return knex.schema.alterTable("event", function (t) { + t.boolean("is_wca_event").notNullable().defaultTo(false); + }); +} + +export async function down(knex: Knex): Promise { + return knex.schema.alterTable("event", function (t) { + t.dropColumn("is_wca_event"); + }); +} diff --git a/backend/functions/migration.ts b/backend/functions/migration.ts index bf8f65f..a265d6e 100644 --- a/backend/functions/migration.ts +++ b/backend/functions/migration.ts @@ -2,19 +2,101 @@ import * as Knex from "knex"; export async function up(knex: Knex): Promise { return Promise.all([ - knex.schema.createTable("userUserFollowLink", function (table) { table.increments();table.integer("user").notNullable();table.integer("target").notNullable();table.dateTime("created_at").notNullable().defaultTo(knex.fn.now());table.dateTime("updated_at").nullable();table.integer("created_by").notNullable();table.unique(["user","target"]); }), -knex.schema.createTable("user", function (table) { table.increments();table.string("provider").notNullable();table.string("provider_id").notNullable();table.string("wca_id").nullable();table.string("email").notNullable().unique();table.string("name").notNullable();table.string("avatar").nullable();table.string("country").nullable();table.boolean("is_public").notNullable().defaultTo(true);table.boolean("is_featured").notNullable().defaultTo(false);table.integer("role").notNullable().defaultTo(2);table.json("permissions").nullable();table.dateTime("created_at").notNullable().defaultTo(knex.fn.now());table.dateTime("updated_at").nullable();table.integer("created_by").notNullable();table.unique(["provider","provider_id"]); }), -knex.schema.createTable("event", function (table) { table.increments();table.string("name").notNullable();table.text("description").nullable();table.string("code").notNullable().unique();table.string("cubing_icon").nullable();table.string("score_method").notNullable();table.dateTime("created_at").notNullable().defaultTo(knex.fn.now());table.dateTime("updated_at").nullable();table.integer("created_by").notNullable(); }), -knex.schema.createTable("product", function (table) { table.increments();table.string("name").notNullable();table.dateTime("created_at").notNullable().defaultTo(knex.fn.now());table.dateTime("updated_at").nullable();table.integer("created_by").notNullable(); }), -knex.schema.createTable("personalBestClass", function (table) { table.increments();table.string("name").notNullable();table.text("description").nullable();table.integer("set_size").nullable();table.dateTime("created_at").notNullable().defaultTo(knex.fn.now());table.dateTime("updated_at").nullable();table.integer("created_by").notNullable(); }), -knex.schema.createTable("personalBest", function (table) { table.increments();table.integer("pb_class").notNullable();table.integer("event").notNullable();table.integer("set_size").notNullable();table.integer("score").notNullable();table.integer("attempts_succeeded").nullable();table.integer("attempts_total").nullable();table.integer("product").nullable();table.dateTime("happened_on").notNullable().defaultTo(knex.fn.now());table.integer("time_elapsed").nullable();table.decimal("moves_count").nullable();table.boolean("is_current").notNullable();table.text("public_comments").nullable();table.dateTime("created_at").notNullable().defaultTo(knex.fn.now());table.dateTime("updated_at").nullable();table.integer("created_by").notNullable(); }), -knex.schema.createTable("apiKey", function (table) { table.increments();table.string("name").notNullable();table.string("code").notNullable().unique();table.integer("user").notNullable();table.json("permissions").nullable();table.dateTime("created_at").notNullable().defaultTo(knex.fn.now());table.dateTime("updated_at").nullable();table.integer("created_by").notNullable(); }), - - ]) + knex.schema.createTable("userUserFollowLink", function (table) { + table.increments(); + table.integer("user").notNullable(); + table.integer("target").notNullable(); + table.dateTime("created_at").notNullable().defaultTo(knex.fn.now()); + table.dateTime("updated_at").nullable(); + table.integer("created_by").notNullable(); + table.unique(["user", "target"]); + }), + knex.schema.createTable("user", function (table) { + table.increments(); + table.string("provider").notNullable(); + table.string("provider_id").notNullable(); + table.string("wca_id").nullable(); + table.string("email").notNullable().unique(); + table.string("name").notNullable(); + table.string("avatar").nullable(); + table.string("country").nullable(); + table.boolean("is_public").notNullable().defaultTo(true); + table.boolean("is_featured").notNullable().defaultTo(false); + table.integer("role").notNullable().defaultTo(2); + table.json("permissions").nullable(); + table.dateTime("created_at").notNullable().defaultTo(knex.fn.now()); + table.dateTime("updated_at").nullable(); + table.integer("created_by").notNullable(); + table.unique(["provider", "provider_id"]); + }), + knex.schema.createTable("event", function (table) { + table.increments(); + table.string("name").notNullable(); + table.text("description").nullable(); + table.string("code").notNullable().unique(); + table.string("cubing_icon").nullable(); + table.string("score_method").notNullable(); + table.boolean("is_sub_event").notNullable().defaultTo(false); + table.boolean("is_wca_event").notNullable().defaultTo(false); + table.dateTime("created_at").notNullable().defaultTo(knex.fn.now()); + table.dateTime("updated_at").nullable(); + table.integer("created_by").notNullable(); + }), + knex.schema.createTable("product", function (table) { + table.increments(); + table.string("name").notNullable(); + table.dateTime("created_at").notNullable().defaultTo(knex.fn.now()); + table.dateTime("updated_at").nullable(); + table.integer("created_by").notNullable(); + }), + knex.schema.createTable("personalBestClass", function (table) { + table.increments(); + table.string("name").notNullable(); + table.text("description").nullable(); + table.integer("set_size").nullable(); + table.dateTime("created_at").notNullable().defaultTo(knex.fn.now()); + table.dateTime("updated_at").nullable(); + table.integer("created_by").notNullable(); + }), + knex.schema.createTable("personalBest", function (table) { + table.increments(); + table.integer("pb_class").notNullable(); + table.integer("event").notNullable(); + table.integer("set_size").notNullable(); + table.integer("score").notNullable(); + table.integer("attempts_succeeded").nullable(); + table.integer("attempts_total").nullable(); + table.integer("product").nullable(); + table.dateTime("happened_on").notNullable().defaultTo(knex.fn.now()); + table.integer("time_elapsed").nullable(); + table.decimal("moves_count").nullable(); + table.boolean("is_current").notNullable(); + table.text("public_comments").nullable(); + table.dateTime("created_at").notNullable().defaultTo(knex.fn.now()); + table.dateTime("updated_at").nullable(); + table.integer("created_by").notNullable(); + }), + knex.schema.createTable("apiKey", function (table) { + table.increments(); + table.string("name").notNullable(); + table.string("code").notNullable().unique(); + table.integer("user").notNullable(); + table.json("permissions").nullable(); + table.dateTime("created_at").notNullable().defaultTo(knex.fn.now()); + table.dateTime("updated_at").nullable(); + table.integer("created_by").notNullable(); + }), + ]); } export async function down(knex: Knex): Promise { return Promise.all([ - knex.schema.dropTable("userUserFollowLink"),knex.schema.dropTable("user"),knex.schema.dropTable("event"),knex.schema.dropTable("product"),knex.schema.dropTable("personalBestClass"),knex.schema.dropTable("personalBest"),knex.schema.dropTable("apiKey") + knex.schema.dropTable("userUserFollowLink"), + knex.schema.dropTable("user"), + knex.schema.dropTable("event"), + knex.schema.dropTable("product"), + knex.schema.dropTable("personalBestClass"), + knex.schema.dropTable("personalBest"), + knex.schema.dropTable("apiKey"), ]); } diff --git a/backend/functions/schema.ts b/backend/functions/schema.ts index f028a85..a4e4795 100644 --- a/backend/functions/schema.ts +++ b/backend/functions/schema.ts @@ -219,6 +219,8 @@ export type FilterByField = { code: Scalars["string"]; cubingIcon?: Scalars["string"] | null; scoreMethod: Scalars["scoreMethod"]; + isSubEvent?: Scalars["boolean"]; + isWcaEvent?: Scalars["boolean"]; }; updateEventFields: { name?: Scalars["string"]; @@ -226,6 +228,8 @@ export type FilterByField = { code?: Scalars["string"]; cubingIcon?: Scalars["string"] | null; scoreMethod?: Scalars["scoreMethod"]; + isSubEvent?: Scalars["boolean"]; + isWcaEvent?: Scalars["boolean"]; }; updateEvent: { item: InputTypes["event"]; @@ -629,6 +633,8 @@ export type UserUserFollowLinkEdge = Edge; code: { Type: Scalars["string"]; Args: undefined }; cubingIcon: { Type: Scalars["string"] | null; Args: undefined }; scoreMethod: { Type: Scalars["scoreMethod"]; Args: undefined }; + isSubEvent: { Type: Scalars["boolean"]; Args: undefined }; + isWcaEvent: { Type: Scalars["boolean"]; Args: undefined }; /**When the record was created*/ createdAt: { Type: Scalars["unixTimestamp"]; Args: undefined; diff --git a/backend/functions/src/schema/models/event/typeDef.ts b/backend/functions/src/schema/models/event/typeDef.ts index c2d68aa..fa956ab 100644 --- a/backend/functions/src/schema/models/event/typeDef.ts +++ b/backend/functions/src/schema/models/event/typeDef.ts @@ -9,6 +9,7 @@ import { generateTypenameField, generateEnumField, generateTextField, + generateBooleanField, } from "../../core/helpers/typeDef"; import * as Scalars from "../../scalars"; @@ -37,6 +38,20 @@ export default new GiraffeqlObjectType({ allowNull: false, sqlOptions: { field: "score_method" }, }), + isSubEvent: generateBooleanField({ + allowNull: false, + defaultValue: false, + sqlOptions: { + field: "is_sub_event", + }, + }), + isWcaEvent: generateBooleanField({ + allowNull: false, + defaultValue: false, + sqlOptions: { + field: "is_wca_event", + }, + }), ...generateCreatedAtField(), ...generateUpdatedAtField(), ...generateCreatedByField(User), diff --git a/frontend/components/table/common/eventColumn.vue b/frontend/components/table/common/eventColumn.vue index ec01dad..8eeea18 100644 --- a/frontend/components/table/common/eventColumn.vue +++ b/frontend/components/table/common/eventColumn.vue @@ -11,7 +11,12 @@ @@ -93,6 +111,8 @@ export default { getEvent: { description: true, scoreMethod: true, + isSubEvent: true, + isWcaEvent: true, __args: { id: this.currentValue.id, }, diff --git a/frontend/models/event.ts b/frontend/models/event.ts index 1588615..b6731fa 100644 --- a/frontend/models/event.ts +++ b/frontend/models/event.ts @@ -40,6 +40,16 @@ export const Event = >{ getOptions: getScoreMethods, inputType: 'select', }, + isSubEvent: { + text: 'Sub Event', + inputType: 'switch', + parseQueryValue: (val) => val === 'true', + }, + isWcaEvent: { + text: 'WCA Event', + inputType: 'switch', + parseQueryValue: (val) => val === 'true', + }, createdAt: { text: 'Created At', component: TimeagoColumn, @@ -57,6 +67,16 @@ export const Event = >{ field: 'name+code', sortable: true, }, + { + field: 'isSubEvent', + sortable: false, + width: '150px', + }, + { + field: 'isWcaEvent', + sortable: false, + width: '150px', + }, { field: 'scoreMethod', sortable: false, @@ -86,13 +106,29 @@ export const Event = >{ downloadOptions: {}, }, addOptions: { - fields: ['name', 'description', 'code', 'cubingIcon', 'scoreMethod'], + fields: [ + 'name', + 'description', + 'code', + 'cubingIcon', + 'scoreMethod', + 'isSubEvent', + 'isWcaEvent', + ], }, editOptions: { - fields: ['name', 'description', 'code', 'cubingIcon'], + fields: ['name', 'description', 'code', 'cubingIcon', 'isWcaEvent'], }, viewOptions: { - fields: ['name', 'description', 'code', 'cubingIcon', 'scoreMethod'], + fields: [ + 'name', + 'description', + 'code', + 'cubingIcon', + 'scoreMethod', + 'isSubEvent', + 'isWcaEvent', + ], }, deleteOptions: {}, shareOptions: undefined, diff --git a/frontend/models/personalBest.ts b/frontend/models/personalBest.ts index 450785b..3da734f 100644 --- a/frontend/models/personalBest.ts +++ b/frontend/models/personalBest.ts @@ -56,7 +56,7 @@ export const PersonalBest = >{ 'event.cubingIcon': { text: 'Event Icon', }, - 'event.name+event.cubingIcon': { + 'event.id+event.name+event.cubingIcon': { text: 'Event', compoundOptions: { pathPrefix: 'event', @@ -293,7 +293,7 @@ export const PersonalBest = >{ ], headers: [ { - field: 'event.name+event.cubingIcon', + field: 'event.id+event.name+event.cubingIcon', width: '200px', }, { @@ -355,7 +355,7 @@ export const PersonalBest = >{ }, viewOptions: { fields: [ - 'event.name+event.cubingIcon', + 'event.id+event.name+event.cubingIcon', 'pbClass.name+setSize', 'score+timeElapsed+movesCount+attemptsSucceeded+attemptsTotal+event.scoreMethod', 'ranking', diff --git a/frontend/types/schema.ts b/frontend/types/schema.ts index 025d9bf..5dfee4a 100644 --- a/frontend/types/schema.ts +++ b/frontend/types/schema.ts @@ -214,6 +214,8 @@ export type FilterByField = { code: Scalars['string'] cubingIcon?: Scalars['string'] | null scoreMethod: Scalars['scoreMethod'] + isSubEvent?: Scalars['boolean'] + isWcaEvent?: Scalars['boolean'] } updateEventFields: { name?: Scalars['string'] @@ -221,6 +223,8 @@ export type FilterByField = { code?: Scalars['string'] cubingIcon?: Scalars['string'] | null scoreMethod?: Scalars['scoreMethod'] + isSubEvent?: Scalars['boolean'] + isWcaEvent?: Scalars['boolean'] } updateEvent: { item: InputTypes['event'] @@ -624,6 +628,8 @@ export type UserUserFollowLinkEdge = Edge code: { Type: Scalars['string']; Args: undefined } cubingIcon: { Type: Scalars['string'] | null; Args: undefined } scoreMethod: { Type: Scalars['scoreMethod']; Args: undefined } + isSubEvent: { Type: Scalars['boolean']; Args: undefined } + isWcaEvent: { Type: Scalars['boolean']; Args: undefined } /**When the record was created*/ createdAt: { Type: Scalars['unixTimestamp'] Args: undefined