diff --git a/ddl/Tables/ddl-internal-applications.sql b/ddl/Tables/ddl-internal-applications.sql new file mode 100644 index 0000000..170a6b0 --- /dev/null +++ b/ddl/Tables/ddl-internal-applications.sql @@ -0,0 +1,71 @@ +CREATE TYPE virginia_dev_saayam_rdbms.internal_app_status_type + AS ENUM ('PENDING', 'IN_VERIFICATION', 'APPROVED', 'REJECTED'); + +CREATE TABLE virginia_dev_saayam_rdbms.internal_applications ( + user_id VARCHAR(255) PRIMARY KEY, + + -- contact / identity + email VARCHAR(255) NOT NULL, + first_name VARCHAR(100) NOT NULL, + last_name VARCHAR(100) NOT NULL, + phone_country_code VARCHAR(10), + phone_number VARCHAR(20), + phone_extension VARCHAR(10), + linkedin_url VARCHAR(500), + github_url VARCHAR(500), + timezone VARCHAR(100), + + -- academic / professional background + college VARCHAR(255), + degree_program VARCHAR(255), + dean_contact VARCHAR(255), + relevant_experience TEXT, + + -- role / engagement + preferred_role VARCHAR(100), + hours_per_week INT, + start_date DATE, + engagement_type VARCHAR(50), + + -- location & document type + is_us_based BOOLEAN, + need_offer_letter BOOLEAN, + document_type VARCHAR(50), + country VARCHAR(100), + + -- S3 document references + resume_s3_key VARCHAR(500), + ead_card_s3_key VARCHAR(500), + i20_s3_key VARCHAR(500), + government_id_s3_key VARCHAR(500), + govt_id_path_updated_at TIMESTAMP WITHOUT TIME ZONE + DEFAULT (now() AT TIME ZONE 'UTC'), + + -- acknowledgments + acknowledgments JSONB, + acknowledgments_accepted_at TIMESTAMP WITHOUT TIME ZONE + DEFAULT (now() AT TIME ZONE 'UTC'), + + -- review workflow + application_status virginia_dev_saayam_rdbms.internal_app_status_type + DEFAULT 'PENDING', + reviewer_cognito_id VARCHAR(255), + reviewer_notes TEXT, + rejection_reason TEXT, + + -- timestamps + created_at TIMESTAMP WITHOUT TIME ZONE + DEFAULT (now() AT TIME ZONE 'UTC'), + last_updated_at TIMESTAMP WITHOUT TIME ZONE + DEFAULT (now() AT TIME ZONE 'UTC'), + reviewed_at TIMESTAMP WITHOUT TIME ZONE, + + FOREIGN KEY (user_id) REFERENCES virginia_dev_saayam_rdbms.users(user_id) +); + +CREATE INDEX idx_internal_apps_status + ON virginia_dev_saayam_rdbms.internal_applications(application_status); +CREATE INDEX idx_internal_apps_reviewer + ON virginia_dev_saayam_rdbms.internal_applications(reviewer_cognito_id); +CREATE INDEX idx_internal_apps_created + ON virginia_dev_saayam_rdbms.internal_applications(created_at DESC); diff --git a/ddl/Tables/ddl-internal_app_rejections.sql b/ddl/Tables/ddl-internal_app_rejections.sql new file mode 100644 index 0000000..dd6613a --- /dev/null +++ b/ddl/Tables/ddl-internal_app_rejections.sql @@ -0,0 +1,44 @@ +CREATE TABLE virginia_dev_saayam_rdbms.internal_app_rejections ( + rejection_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id VARCHAR(255) NOT NULL, + + -- applicant snapshot + email VARCHAR(255) NOT NULL, + first_name VARCHAR(100), + last_name VARCHAR(100), + phone_country_code VARCHAR(10), + phone_number VARCHAR(20), + linkedin_url VARCHAR(500), + github_url VARCHAR(500), + college VARCHAR(255), + degree_program VARCHAR(255), + relevant_experience TEXT, + preferred_role VARCHAR(100), + hours_per_week INT, + start_date DATE, + engagement_type VARCHAR(50), + is_us_based BOOLEAN, + country VARCHAR(100), + resume_s3_key VARCHAR(500), + ead_card_s3_key VARCHAR(500), + i20_s3_key VARCHAR(500), + government_id_s3_key VARCHAR(500), + acknowledgments JSONB, + + -- rejection metadata + rejection_reason TEXT NOT NULL, + reviewer_cognito_id VARCHAR(255), + reviewer_notes TEXT, + + -- timestamps + application_created_at TIMESTAMP WITHOUT TIME ZONE, + rejected_at TIMESTAMP WITHOUT TIME ZONE + DEFAULT (now() AT TIME ZONE 'UTC'), + + FOREIGN KEY (user_id) REFERENCES virginia_dev_saayam_rdbms.users(user_id) +); + +CREATE INDEX idx_internal_rejections_user + ON virginia_dev_saayam_rdbms.internal_app_rejections(user_id); +CREATE INDEX idx_internal_rejections_rejected_at + ON virginia_dev_saayam_rdbms.internal_app_rejections(rejected_at DESC); diff --git a/ddl/Tables/ddl_handle_internal_application.sql b/ddl/Tables/ddl_handle_internal_application.sql new file mode 100644 index 0000000..28339f3 --- /dev/null +++ b/ddl/Tables/ddl_handle_internal_application.sql @@ -0,0 +1,104 @@ +-- ===================================================================== +-- 2. handle_internal_application +-- Routes the row to internal_team or internal_app_rejections based +-- on terminal application_status, then deletes the source row. +-- Mirrors handle_volunteer_application but supports TWO exits. +-- ===================================================================== +CREATE OR REPLACE FUNCTION virginia_dev_saayam_rdbms.handle_internal_application() +RETURNS TRIGGER AS $$ +BEGIN + -- ---- APPROVED path -> internal_team ---- + IF (NEW.application_status = 'APPROVED' + AND OLD.application_status != 'APPROVED') THEN + + INSERT INTO virginia_dev_saayam_rdbms.internal_team ( + user_id, email, first_name, last_name, + phone_country_code, phone_number, phone_extension, + linkedin_url, github_url, timezone, + internal_role, preferred_role, hours_per_week, start_date, engagement_type, + is_us_based, country, government_id_s3_key, + is_active, joined_at, + created_at, last_updated_at + ) VALUES ( + NEW.user_id, NEW.email, NEW.first_name, NEW.last_name, + NEW.phone_country_code, NEW.phone_number, NEW.phone_extension, + NEW.linkedin_url, NEW.github_url, NEW.timezone, + NEW.preferred_role, -- default internal_role = applied-for role + NEW.preferred_role, NEW.hours_per_week, NEW.start_date, NEW.engagement_type, + NEW.is_us_based, NEW.country, NEW.government_id_s3_key, + TRUE, (now() AT TIME ZONE 'UTC'), + NEW.last_updated_at, NEW.last_updated_at + ); + + DELETE FROM virginia_dev_saayam_rdbms.internal_applications + WHERE user_id = NEW.user_id; + + -- ---- REJECTED path -> internal_app_rejections ---- + ELSIF (NEW.application_status = 'REJECTED' + AND OLD.application_status != 'REJECTED') THEN + + INSERT INTO virginia_dev_saayam_rdbms.internal_app_rejections ( + user_id, email, first_name, last_name, + phone_country_code, phone_number, phone_extension, + linkedin_url, github_url, + college, degree_program, relevant_experience, + preferred_role, hours_per_week, start_date, engagement_type, + is_us_based, country, + resume_s3_key, ead_card_s3_key, i20_s3_key, government_id_s3_key, + acknowledgments, + rejection_reason, reviewer_cognito_id, reviewer_notes, + application_created_at, rejected_at + ) VALUES ( + NEW.user_id, NEW.email, NEW.first_name, NEW.last_name, + NEW.phone_country_code, NEW.phone_number, NEW.phone_extension, + NEW.linkedin_url, NEW.github_url, + NEW.college, NEW.degree_program, NEW.relevant_experience, + NEW.preferred_role, NEW.hours_per_week, NEW.start_date, NEW.engagement_type, + NEW.is_us_based, NEW.country, + NEW.resume_s3_key, NEW.ead_card_s3_key, NEW.i20_s3_key, NEW.government_id_s3_key, + NEW.acknowledgments, + COALESCE(NEW.rejection_reason, 'No reason supplied'), + NEW.reviewer_cognito_id, NEW.reviewer_notes, + NEW.created_at, (now() AT TIME ZONE 'UTC') + ); + + DELETE FROM virginia_dev_saayam_rdbms.internal_applications + WHERE user_id = NEW.user_id; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + + +-- ===================================================================== +-- 3. Trigger definitions +-- ===================================================================== + +-- BEFORE UPDATE: auto-timestamps on internal_applications +CREATE TRIGGER trg_internal_app_updated_at + BEFORE UPDATE ON virginia_dev_saayam_rdbms.internal_applications + FOR EACH ROW EXECUTE FUNCTION virginia_dev_saayam_rdbms.updated_at_handler(); + +-- BEFORE UPDATE: auto-timestamps on internal_team +CREATE TRIGGER trg_internal_team_updated_at + BEFORE UPDATE ON virginia_dev_saayam_rdbms.internal_team + FOR EACH ROW EXECUTE FUNCTION virginia_dev_saayam_rdbms.updated_at_handler(); + +-- AFTER UPDATE: route on terminal application_status +CREATE TRIGGER trg_handle_internal_application + AFTER UPDATE ON virginia_dev_saayam_rdbms.internal_applications + FOR EACH ROW EXECUTE FUNCTION virginia_dev_saayam_rdbms.handle_internal_application(); + + +-- ===================================================================== +-- 4. Explicit ENABLE (matches existing volunteer file convention) +-- ===================================================================== +ALTER TABLE virginia_dev_saayam_rdbms.internal_applications + ENABLE TRIGGER trg_internal_app_updated_at; + +ALTER TABLE virginia_dev_saayam_rdbms.internal_applications + ENABLE TRIGGER trg_handle_internal_application; + +ALTER TABLE virginia_dev_saayam_rdbms.internal_team + ENABLE TRIGGER trg_internal_team_updated_at; diff --git a/ddl/Tables/ddl_internal_teams.sql b/ddl/Tables/ddl_internal_teams.sql new file mode 100644 index 0000000..ec25588 --- /dev/null +++ b/ddl/Tables/ddl_internal_teams.sql @@ -0,0 +1,44 @@ +CREATE TABLE virginia_dev_saayam_rdbms.internal_team ( + user_id VARCHAR(255) PRIMARY KEY, + + -- identity + email VARCHAR(255) NOT NULL, + first_name VARCHAR(100) NOT NULL, + last_name VARCHAR(100) NOT NULL, + phone_country_code VARCHAR(10), + phone_number VARCHAR(20), + linkedin_url VARCHAR(500), + github_url VARCHAR(500), + timezone VARCHAR(100), + + -- role / engagement + internal_role VARCHAR(100), + preferred_role VARCHAR(100), + hours_per_week INT, + start_date DATE, + engagement_type VARCHAR(50), + + -- location / docs + is_us_based BOOLEAN, + country VARCHAR(100), + government_id_s3_key VARCHAR(500), + + -- membership lifecycle + is_active BOOLEAN DEFAULT TRUE, + joined_at TIMESTAMP WITHOUT TIME ZONE + DEFAULT (now() AT TIME ZONE 'UTC'), + deactivated_at TIMESTAMP WITHOUT TIME ZONE, + + -- standard timestamps + created_at TIMESTAMP WITHOUT TIME ZONE + DEFAULT (now() AT TIME ZONE 'UTC'), + last_updated_at TIMESTAMP WITHOUT TIME ZONE + DEFAULT (now() AT TIME ZONE 'UTC'), + + FOREIGN KEY (user_id) REFERENCES virginia_dev_saayam_rdbms.users(user_id) +); + +CREATE INDEX idx_internal_team_role + ON virginia_dev_saayam_rdbms.internal_team(internal_role); +CREATE INDEX idx_internal_team_active + ON virginia_dev_saayam_rdbms.internal_team(is_active); diff --git a/ddl/Tables/ddl_updated_at_handler.sql b/ddl/Tables/ddl_updated_at_handler.sql new file mode 100644 index 0000000..070d806 --- /dev/null +++ b/ddl/Tables/ddl_updated_at_handler.sql @@ -0,0 +1,55 @@ +CREATE OR REPLACE FUNCTION virginia_dev_saayam_rdbms.updated_at_handler() +RETURNS TRIGGER AS $$ +BEGIN + -- If last_updated_at wasn't explicitly changed by the API, force UTC now. + IF (NEW.last_updated_at IS NOT DISTINCT FROM OLD.last_updated_at) THEN + NEW.last_updated_at = (now() AT TIME ZONE 'UTC'); + END IF; + + -- existing: volunteer_applications + IF (TG_TABLE_NAME = 'volunteer_applications') THEN + IF (NEW.govt_id_path IS DISTINCT FROM OLD.govt_id_path) THEN + NEW.path_updated_at = (now() AT TIME ZONE 'UTC'); + END IF; + IF (NEW.terms_and_conditions IS TRUE + AND (OLD.terms_and_conditions IS FALSE OR OLD.terms_and_conditions IS NULL)) THEN + NEW.terms_accepted_at = (now() AT TIME ZONE 'UTC'); + END IF; + + -- existing: volunteer_details + ELSIF (TG_TABLE_NAME = 'volunteer_details') THEN + IF (NEW.govt_id_path1 IS DISTINCT FROM OLD.govt_id_path1) THEN + NEW.path1_updated_at = (now() AT TIME ZONE 'UTC'); + END IF; + IF (NEW.govt_id_path2 IS DISTINCT FROM OLD.govt_id_path2) THEN + NEW.path2_updated_at = (now() AT TIME ZONE 'UTC'); + END IF; + IF (NEW.terms_and_conditions IS TRUE + AND (OLD.terms_and_conditions IS FALSE OR OLD.terms_and_conditions IS NULL)) THEN + NEW.terms_accepted_at = (now() AT TIME ZONE 'UTC'); + END IF; + + -- NEW: internal_applications + ELSIF (TG_TABLE_NAME = 'internal_applications') THEN + IF (NEW.government_id_s3_key IS DISTINCT FROM OLD.government_id_s3_key) THEN + NEW.govt_id_path_updated_at = (now() AT TIME ZONE 'UTC'); + END IF; + IF (NEW.acknowledgments IS DISTINCT FROM OLD.acknowledgments) THEN + NEW.acknowledgments_accepted_at = (now() AT TIME ZONE 'UTC'); + END IF; + IF (NEW.application_status IS DISTINCT FROM OLD.application_status + AND NEW.application_status IN ('APPROVED', 'REJECTED')) THEN + NEW.reviewed_at = (now() AT TIME ZONE 'UTC'); + END IF; + + -- NEW: internal_team + ELSIF (TG_TABLE_NAME = 'internal_team') THEN + IF (NEW.is_active IS FALSE + AND (OLD.is_active IS TRUE OR OLD.is_active IS NULL)) THEN + NEW.deactivated_at = (now() AT TIME ZONE 'UTC'); + END IF; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; diff --git a/ddl/Tables/ddl_volunteer_applications.sql b/ddl/Tables/ddl_volunteer_applications.sql index 655f346..37605e5 100644 --- a/ddl/Tables/ddl_volunteer_applications.sql +++ b/ddl/Tables/ddl_volunteer_applications.sql @@ -21,88 +21,152 @@ CREATE TABLE virginia_dev_saayam_rdbms.volunteer_applications ( CREATE OR REPLACE FUNCTION virginia_dev_saayam_rdbms.updated_at_handler() RETURNS TRIGGER AS $$ BEGIN - -- check if the last_updated_at was explicitly changed in the UPDATE statement. - -- if it WASN'T changed by the API, we force it to the current UTC. + -- Global: if API didn't explicitly set last_updated_at, force UTC now. IF (NEW.last_updated_at IS NOT DISTINCT FROM OLD.last_updated_at) THEN NEW.last_updated_at = (now() AT TIME ZONE 'UTC'); END IF; - - -- handle application table + + -- EXISTING: volunteer_applications IF (TG_TABLE_NAME = 'volunteer_applications') THEN - -- Update path timestamp if path changed IF (NEW.govt_id_path IS DISTINCT FROM OLD.govt_id_path) THEN NEW.path_updated_at = (now() AT TIME ZONE 'UTC'); END IF; - - -- Update terms timestamp if accepted - IF (NEW.terms_and_conditions IS TRUE AND (OLD.terms_and_conditions IS FALSE OR OLD.terms_and_conditions IS NULL)) THEN + IF (NEW.terms_and_conditions IS TRUE + AND (OLD.terms_and_conditions IS FALSE OR OLD.terms_and_conditions IS NULL)) THEN NEW.terms_accepted_at = (now() AT TIME ZONE 'UTC'); END IF; - - -- handle details table + + -- EXISTING: volunteer_details ELSIF (TG_TABLE_NAME = 'volunteer_details') THEN - -- update path1 timestamp if changed IF (NEW.govt_id_path1 IS DISTINCT FROM OLD.govt_id_path1) THEN NEW.path1_updated_at = (now() AT TIME ZONE 'UTC'); END IF; - - -- update path2 timestamp if changed IF (NEW.govt_id_path2 IS DISTINCT FROM OLD.govt_id_path2) THEN NEW.path2_updated_at = (now() AT TIME ZONE 'UTC'); END IF; - - -- update terms timestamp if accepted - IF (NEW.terms_and_conditions IS TRUE AND (OLD.terms_and_conditions IS FALSE OR OLD.terms_and_conditions IS NULL)) THEN + IF (NEW.terms_and_conditions IS TRUE + AND (OLD.terms_and_conditions IS FALSE OR OLD.terms_and_conditions IS NULL)) THEN NEW.terms_accepted_at = (now() AT TIME ZONE 'UTC'); END IF; + + -- NEW: internal_applications + ELSIF (TG_TABLE_NAME = 'internal_applications') THEN + -- Stamp govt_id_path_updated_at when the S3 key changes + IF (NEW.government_id_s3_key IS DISTINCT FROM OLD.government_id_s3_key) THEN + NEW.govt_id_path_updated_at = (now() AT TIME ZONE 'UTC'); + END IF; + + -- Stamp acknowledgments_accepted_at when consent JSON changes + IF (NEW.acknowledgments IS DISTINCT FROM OLD.acknowledgments) THEN + NEW.acknowledgments_accepted_at = (now() AT TIME ZONE 'UTC'); + END IF; + + -- Stamp reviewed_at when status hits a terminal state + IF (NEW.application_status IS DISTINCT FROM OLD.application_status + AND NEW.application_status IN ('APPROVED', 'REJECTED')) THEN + NEW.reviewed_at = (now() AT TIME ZONE 'UTC'); + END IF; + + -- NEW: internal_team + ELSIF (TG_TABLE_NAME = 'internal_team') THEN + -- Stamp deactivated_at when is_active flips from TRUE/NULL to FALSE + IF (NEW.is_active IS FALSE + AND (OLD.is_active IS TRUE OR OLD.is_active IS NULL)) THEN + NEW.deactivated_at = (now() AT TIME ZONE 'UTC'); + END IF; END IF; - + RETURN NEW; END; $$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION virginia_dev_saayam_rdbms.handle_volunteer_application() +CREATE OR REPLACE FUNCTION virginia_dev_saayam_rdbms.handle_internal_application() RETURNS TRIGGER AS $$ BEGIN - IF (NEW.application_status = 'ACCEPTED' AND OLD.application_status != 'ACCEPTED') THEN - - -- Migrate to volunteer_details - INSERT INTO virginia_dev_saayam_rdbms.volunteer_details ( - user_id, terms_and_conditions, terms_accepted_at, - govt_id_path1, path1_updated_at, path2_updated_at, availability_days, availability_times, + -- ---- APPROVED path ---- + IF (NEW.application_status = 'APPROVED' + AND OLD.application_status != 'APPROVED') THEN + + INSERT INTO virginia_dev_saayam_rdbms.internal_team ( + user_id, email, first_name, last_name, + phone_country_code, phone_number, + linkedin_url, github_url, timezone, + internal_role, preferred_role, hours_per_week, start_date, engagement_type, + is_us_based, country, government_id_s3_key, + is_active, joined_at, created_at, last_updated_at ) VALUES ( - NEW.user_id, NEW.terms_and_conditions, NEW.terms_accepted_at, - NEW.govt_id_path, NEW.path_updated_at, NULL, (NEW.availability -> 'days')::JSONB, (NEW.availability -> 'time')::JSONB, + NEW.user_id, NEW.email, NEW.first_name, NEW.last_name, + NEW.phone_country_code, NEW.phone_number, + NEW.linkedin_url, NEW.github_url, NEW.timezone, + NEW.preferred_role, -- default internal_role = what they applied for + NEW.preferred_role, NEW.hours_per_week, NEW.start_date, NEW.engagement_type, + NEW.is_us_based, NEW.country, NEW.government_id_s3_key, + TRUE, (now() AT TIME ZONE 'UTC'), NEW.last_updated_at, NEW.last_updated_at ); - -- Migrate to user_skills (unrolling the JSON array) - IF (NEW.skill_codes IS NOT NULL) THEN - INSERT INTO virginia_dev_saayam_rdbms.user_skills ( - user_id, cat_id, created_at, last_updated_at - ) - SELECT - NEW.user_id, - skill_id, - NEW.last_updated_at, - NEW.last_updated_at - FROM json_array_elements_text(NEW.skill_codes) AS skill_id; - END IF; + DELETE FROM virginia_dev_saayam_rdbms.internal_applications + WHERE user_id = NEW.user_id; - DELETE FROM virginia_dev_saayam_rdbms.volunteer_applications WHERE user_id = NEW.user_id; + -- ---- REJECTED path ---- + ELSIF (NEW.application_status = 'REJECTED' + AND OLD.application_status != 'REJECTED') THEN + + INSERT INTO virginia_dev_saayam_rdbms.internal_app_rejections ( + user_id, email, first_name, last_name, + phone_country_code, phone_number, + linkedin_url, github_url, + college, degree_program, relevant_experience, + preferred_role, hours_per_week, start_date, engagement_type, + is_us_based, country, + resume_s3_key, ead_card_s3_key, i20_s3_key, government_id_s3_key, + acknowledgments, + rejection_reason, reviewer_cognito_id, reviewer_notes, + application_created_at, rejected_at + ) VALUES ( + NEW.user_id, NEW.email, NEW.first_name, NEW.last_name, + NEW.phone_country_code, NEW.phone_number, + NEW.linkedin_url, NEW.github_url, + NEW.college, NEW.degree_program, NEW.relevant_experience, + NEW.preferred_role, NEW.hours_per_week, NEW.start_date, NEW.engagement_type, + NEW.is_us_based, NEW.country, + NEW.resume_s3_key, NEW.ead_card_s3_key, NEW.i20_s3_key, NEW.government_id_s3_key, + NEW.acknowledgments, + COALESCE(NEW.rejection_reason, 'No reason supplied'), + NEW.reviewer_cognito_id, NEW.reviewer_notes, + NEW.created_at, (now() AT TIME ZONE 'UTC') + ); + + DELETE FROM virginia_dev_saayam_rdbms.internal_applications + WHERE user_id = NEW.user_id; END IF; - + RETURN NEW; END; $$ LANGUAGE plpgsql; -CREATE TRIGGER trg_volunteer_app_updated_at - BEFORE UPDATE ON virginia_dev_saayam_rdbms.volunteer_applications + +CREATE TRIGGER trg_internal_app_updated_at + BEFORE UPDATE ON virginia_dev_saayam_rdbms.internal_applications + FOR EACH ROW EXECUTE FUNCTION virginia_dev_saayam_rdbms.updated_at_handler(); + +-- BEFORE UPDATE: auto-timestamps on internal_team +CREATE TRIGGER trg_internal_team_updated_at + BEFORE UPDATE ON virginia_dev_saayam_rdbms.internal_team FOR EACH ROW EXECUTE FUNCTION virginia_dev_saayam_rdbms.updated_at_handler(); -CREATE TRIGGER trg_handle_volunteer_application - AFTER UPDATE ON virginia_dev_saayam_rdbms.volunteer_applications - FOR EACH ROW EXECUTE FUNCTION virginia_dev_saayam_rdbms.handle_volunteer_application(); +-- AFTER UPDATE: route on terminal application_status +CREATE TRIGGER trg_handle_internal_application + AFTER UPDATE ON virginia_dev_saayam_rdbms.internal_applications + FOR EACH ROW EXECUTE FUNCTION virginia_dev_saayam_rdbms.handle_internal_application(); + +-- 4. Explicit ENABLE (matches existing volunteer file convention) +ALTER TABLE virginia_dev_saayam_rdbms.internal_applications + ENABLE TRIGGER trg_internal_app_updated_at; + +ALTER TABLE virginia_dev_saayam_rdbms.internal_applications + ENABLE TRIGGER trg_handle_internal_application; -ALTER TABLE virginia_dev_saayam_rdbms.volunteer_applications ENABLE TRIGGER trg_volunteer_app_updated_at; +ALTER TABLE virginia_dev_saayam_rdbms.internal_team + ENABLE TRIGGER trg_internal_team_updated_at;