diff --git a/apps/api/migrations/versions/0314ec7791e1_payments.py b/apps/api/migrations/versions/0314ec7791e1_payments.py new file mode 100644 index 00000000..91d71a87 --- /dev/null +++ b/apps/api/migrations/versions/0314ec7791e1_payments.py @@ -0,0 +1,90 @@ +"""Payments + +Revision ID: 0314ec7791e1 +Revises: 040ccb1d456e +Create Date: 2024-11-23 19:41:14.064680 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa # noqa: F401 +import sqlmodel # noqa: F401 +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = '0314ec7791e1' +down_revision: Union[str, None] = '040ccb1d456e' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('paymentsconfig', + sa.Column('enabled', sa.Boolean(), nullable=False), + sa.Column('active', sa.Boolean(), nullable=False), + sa.Column('provider', postgresql.ENUM('STRIPE', name='paymentproviderenum', create_type=False), nullable=False), + sa.Column('provider_specific_id', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('provider_config', sa.JSON(), nullable=True), + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('org_id', sa.BigInteger(), nullable=True), + sa.Column('creation_date', sa.DateTime(), nullable=False), + sa.Column('update_date', sa.DateTime(), nullable=False), + sa.ForeignKeyConstraint(['org_id'], ['organization.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('paymentsproduct', + sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('description', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('product_type', postgresql.ENUM('SUBSCRIPTION', 'ONE_TIME', name='paymentproducttypeenum', create_type=False), nullable=False), + sa.Column('price_type', postgresql.ENUM('CUSTOMER_CHOICE', 'FIXED_PRICE', name='paymentpricetypeenum', create_type=False), nullable=False), + sa.Column('benefits', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('amount', sa.Float(), nullable=False), + sa.Column('currency', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('org_id', sa.BigInteger(), nullable=True), + sa.Column('payments_config_id', sa.BigInteger(), nullable=True), + sa.Column('provider_product_id', sa.String(), nullable=True), + sa.Column('creation_date', sa.DateTime(), nullable=False), + sa.Column('update_date', sa.DateTime(), nullable=False), + sa.ForeignKeyConstraint(['org_id'], ['organization.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['payments_config_id'], ['paymentsconfig.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('paymentscourse', + sa.Column('course_id', sa.BigInteger(), nullable=True), + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('payment_product_id', sa.BigInteger(), nullable=True), + sa.Column('org_id', sa.BigInteger(), nullable=True), + sa.Column('creation_date', sa.DateTime(), nullable=False), + sa.Column('update_date', sa.DateTime(), nullable=False), + sa.ForeignKeyConstraint(['course_id'], ['course.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['org_id'], ['organization.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['payment_product_id'], ['paymentsproduct.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('paymentsuser', + sa.Column('status', postgresql.ENUM('PENDING', 'COMPLETED', 'ACTIVE', 'CANCELLED', 'FAILED', 'REFUNDED', name='paymentstatusenum', create_type=False), nullable=False), + sa.Column('provider_specific_data', sa.JSON(), nullable=True), + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.BigInteger(), nullable=True), + sa.Column('org_id', sa.BigInteger(), nullable=True), + sa.Column('payment_product_id', sa.BigInteger(), nullable=True), + sa.Column('creation_date', sa.DateTime(), nullable=False), + sa.Column('update_date', sa.DateTime(), nullable=False), + sa.ForeignKeyConstraint(['org_id'], ['organization.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['payment_product_id'], ['paymentsproduct.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('paymentsuser') + op.drop_table('paymentscourse') + op.drop_table('paymentsproduct') + op.drop_table('paymentsconfig') + # ### end Alembic commands ### diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx index a5c64371..fa37a3aa 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx @@ -26,7 +26,7 @@ import toast from 'react-hot-toast' import { mutate } from 'swr' import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal' import { useMediaQuery } from 'usehooks-ts' -import PaidCourseActivity from '@components/Objects/Courses/CourseActions/PaidCourseActivity' +import PaidCourseActivityDisclaimer from '@components/Objects/Courses/CourseActions/PaidCourseActivityDisclaimer' interface ActivityClientProps { activityid: string @@ -81,7 +81,6 @@ function ActivityClient(props: ActivityClientProps) { else { setBgColor('bg-zinc-950'); } - console.log(activity.content) } , [activity, pathname]) @@ -175,45 +174,44 @@ function ActivityClient(props: ActivityClientProps) { )} {activity && activity.published == true && ( -
- {/* Paid Courses */} - {activity.content.paid_access == false && ( - - )} - {/* Activity Types */} -
- {activity.activity_type == 'TYPE_DYNAMIC' && ( - - )} - {/* todo : use apis & streams instead of this */} - {activity.activity_type == 'TYPE_VIDEO' && ( - - )} - {activity.activity_type == 'TYPE_DOCUMENT' && ( - - )} - {activity.activity_type == 'TYPE_ASSIGNMENT' && ( + <> + {activity.content.paid_access == false ? ( + + ) : ( +
+ {/* Activity Types */}
- {assignment ? ( - - - - - - - - ) : ( -
+ {activity.activity_type == 'TYPE_DYNAMIC' && ( + + )} + {activity.activity_type == 'TYPE_VIDEO' && ( + + )} + {activity.activity_type == 'TYPE_DOCUMENT' && ( + + )} + {activity.activity_type == 'TYPE_ASSIGNMENT' && ( +
+ {assignment ? ( + + + + + + + + ) : ( +
+ )} +
)}
- )} -
-
+
+ )} + )} {
} diff --git a/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx b/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx index 642f9162..e167c198 100644 --- a/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx +++ b/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx @@ -7,6 +7,7 @@ import { Eye, File, FilePenLine, + FileSymlink, Globe, Lock, MoreVertical, @@ -27,6 +28,7 @@ import { useOrg } from '@components/Contexts/OrgContext' import { useCourse } from '@components/Contexts/CourseContext' import toast from 'react-hot-toast' import { useMediaQuery } from 'usehooks-ts' +import ToolTip from '@components/StyledElements/Tooltip/Tooltip' type ActivitiyElementProps = { orgslug: string @@ -176,24 +178,26 @@ function ActivityElement(props: ActivitiyElementProps) { )} {!props.activity.published ? 'Publish' : 'Unpublish'} - - - Preview - +
+ + + + + {/* Delete Button */} - {!isMobile && Delete} } functionToExecute={() => deleteActivityUI()} diff --git a/apps/web/components/Objects/Courses/CourseActions/CoursePaidOptions.tsx b/apps/web/components/Objects/Courses/CourseActions/CoursePaidOptions.tsx index 0dd77dfe..c2537497 100644 --- a/apps/web/components/Objects/Courses/CourseActions/CoursePaidOptions.tsx +++ b/apps/web/components/Objects/Courses/CourseActions/CoursePaidOptions.tsx @@ -74,7 +74,7 @@ function CoursePaidOptions({ course }: CoursePaidOptionsProps) {
- + {product.product_type === 'subscription' ? : } {product.product_type === 'subscription' ? 'Subscription' : 'One-time payment'} diff --git a/apps/web/components/Objects/Courses/CourseActions/PaidCourseActivity.tsx b/apps/web/components/Objects/Courses/CourseActions/PaidCourseActivityDisclaimer.tsx similarity index 70% rename from apps/web/components/Objects/Courses/CourseActions/PaidCourseActivity.tsx rename to apps/web/components/Objects/Courses/CourseActions/PaidCourseActivityDisclaimer.tsx index 091bd36b..bb576046 100644 --- a/apps/web/components/Objects/Courses/CourseActions/PaidCourseActivity.tsx +++ b/apps/web/components/Objects/Courses/CourseActions/PaidCourseActivityDisclaimer.tsx @@ -6,16 +6,16 @@ interface PaidCourseActivityProps { course: any; } -function PaidCourseActivity({ course }: PaidCourseActivityProps) { +function PaidCourseActivityDisclaimer({ course }: PaidCourseActivityProps) { return ( -
-
+
+

Paid Content

- This content requires a course purchase to access. Please purchase the course to continue. + This content requires a course purchase to access.

@@ -23,4 +23,4 @@ function PaidCourseActivity({ course }: PaidCourseActivityProps) { ) } -export default PaidCourseActivity \ No newline at end of file +export default PaidCourseActivityDisclaimer \ No newline at end of file