diff --git a/src/simmate/apps/configs.py b/src/simmate/apps/configs.py index 24c59a0ba..14f29bddd 100644 --- a/src/simmate/apps/configs.py +++ b/src/simmate/apps/configs.py @@ -110,6 +110,10 @@ class PriceCatalogConfig(AppConfig): class ProjectManagementConfig(AppConfig): name = "simmate.apps.project_management" + def ready(self): + # signals a wallet creation for new users + import simmate.apps.project_management.signals + class QuantumEspressoConfig(AppConfig): name = "simmate.apps.quantum_espresso" diff --git a/src/simmate/apps/project_management/components/__init__.py b/src/simmate/apps/project_management/components/__init__.py index a48cf0743..c2d04eec3 100644 --- a/src/simmate/apps/project_management/components/__init__.py +++ b/src/simmate/apps/project_management/components/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- +from .mixins import ProjectInput, ProjectManagementInput from .project_form import ProjectForm - -# from .mixins import ProjectInput, ProjectManagementInput -# from .tag_form import TagFormView +from .tag_form import TagForm +from .token_balance_form import TokenBalanceForm diff --git a/src/simmate/apps/project_management/components/mixins/project.py b/src/simmate/apps/project_management/components/mixins/project.py index 3b3bcda4a..e9559cac4 100644 --- a/src/simmate/apps/project_management/components/mixins/project.py +++ b/src/simmate/apps/project_management/components/mixins/project.py @@ -7,11 +7,6 @@ class ProjectInput: - class Meta: - javascript_exclude = ("project_options",) - - project_id = None - @cached_property def project_options(self): projects = Project.objects.order_by("name").values_list("id", "name").all() diff --git a/src/simmate/apps/project_management/components/mixins/project_management.py b/src/simmate/apps/project_management/components/mixins/project_management.py index a337c55b6..16c4733ae 100644 --- a/src/simmate/apps/project_management/components/mixins/project_management.py +++ b/src/simmate/apps/project_management/components/mixins/project_management.py @@ -1,38 +1,26 @@ # -*- coding: utf-8 -*- -from simmate.website.utilities import parse_multiselect - from ...models import Project, Tag from .project import ProjectInput class ProjectManagementInput(ProjectInput): - class Meta: - javascript_exclude = (*ProjectInput.Meta.javascript_exclude,) - - search_inputs = [ - "project_id", - # "tag_ids", - "hypothesis_id", - ] - # ------------------------------------------------------------------------- - # project_id = None --> inheritted - # project_options --> inheritted - project__display = None - _project_obj = None - def set_project_id(self, project_id): - self.project_id = project_id - self.project_obj = Project.objects.get(id=self.project_id) + def on_change_hook__project_id(self): + + project_id = self.form_data["project_id"] + self.project_obj = Project.objects.get(id=project_id) self.tag_options = self.get_tag_options(self.project_obj) self.hypothesis_options = self.get_hypothesis_options(self.project_obj) - self.call("refresh_select2") + self.js_actions = [ + {"refresh_select2": []}, + ] # populate display value for bulk form for p_id, p_name in self.project_options: @@ -40,6 +28,10 @@ def set_project_id(self, project_id): self.project__display = p_name break + # ------------------------------------------------------------------------- + + tag_options: list[tuple] = [] # set dynamically using Project.tag_options + @staticmethod def get_tag_options(project: Project) -> list[tuple]: @@ -59,6 +51,22 @@ def get_tag_options(project: Project) -> list[tuple]: (id, tag_name) for id, tag_name in list(project_tags) + list(generic_tags) ] + tags__display = None + + def on_change_hook__tags__ids(self): + # populate display value for bulk form + tag_ids = self.form_data.get("tags__ids", []) + self.tags__display = "" + for tag_id in tag_ids: + for t_id, t_name in self.tag_options: + if t_id == tag_id: + self.tags__display += f"{t_name}; " + break + + # ------------------------------------------------------------------------- + + hypothesis_options = None + @staticmethod def get_hypothesis_options(project: Project) -> list[tuple]: @@ -68,17 +76,10 @@ def get_hypothesis_options(project: Project) -> list[tuple]: # reformat into tuple of (value, display) return [(id, name) for id, name in tags] - # ------------------------------------------------------------------------- - - hypothesis_id = None hypothesis__display = None - hypothesis_options = None # set using Project.hypothesis_options - - def set_hypothesis_id(self, hypothesis_id): - self.hypothesis_id = hypothesis_id - if hypothesis_id is None: - return + def on_change_hook__hypothesis_id(self): + hypothesis_id = self.form_data["hypothesis_id"] # populate display value for bulk form for h_id, h_name in self.hypothesis_options: if h_id == hypothesis_id: @@ -86,22 +87,3 @@ def set_hypothesis_id(self, hypothesis_id): break # ------------------------------------------------------------------------- - - tag_ids = None - tags__display = None - tag_options = [] # set dynamically using Project.tag_options - - def set_tag_ids(self, tag_ids): - self.tag_ids = parse_multiselect(tag_ids) - if tag_ids is None: - return - - # populate display value for bulk form - self.tags__display = "" - for tag_id in self.tag_ids: - for t_id, t_name in self.tag_options: - if t_id == tag_id: - self.tags__display += f"{t_name}; " - break - - # ------------------------------------------------------------------------- diff --git a/src/simmate/apps/project_management/components/project_form.py b/src/simmate/apps/project_management/components/project_form.py index cfe166574..bc9866b25 100644 --- a/src/simmate/apps/project_management/components/project_form.py +++ b/src/simmate/apps/project_management/components/project_form.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -from django.db import transaction - from simmate.website.htmx.components import DynamicTableForm, UserInput from ..models import Project @@ -11,9 +9,7 @@ class ProjectForm(DynamicTableForm, UserInput): table = Project - template_names = dict( - default="project_management/project/form.html", - ) + template_name = "project_management/project/form.html" # ------------------------------------------------------------------------- @@ -24,8 +20,8 @@ class ProjectForm(DynamicTableForm, UserInput): "description", "discipline", "status", - "leader_ids", - "member_ids", + "leaders__ids", + "members__ids", ] def check_form_for_create(self): @@ -40,32 +36,21 @@ def check_form_for_create(self): f"A project with the name '{project_name}' already exists." ) - def save_to_db_for_create(self): - # We are saving multiple objects, so we want to make sure ALL work. - # If not, we roll back everything - with transaction.atomic(): - # Save the request to the database - self.table_entry.save() - # add leaders and members - self.table_entry.leaders.set(self.form_data["leader_ids"]) - self.table_entry.members.set(self.form_data["member_ids"]) - # ------------------------------------------------------------------------- # UPDATE - def mount_for_update(self): - super().mount_for_update() - self.form_data["leader_ids"] = list( - self.table_entry.leaders.values_list("id", flat=True).all() - ) - self.form_data["member_ids"] = list( - self.table_entry.members.values_list("id", flat=True).all() - ) + mount_for_update_columns = [ + "name", + "description", + "discipline", + "status", + "leaders__ids", + "members__ids", + ] def check_form_for_update(self): self.check_required_inputs() - # if the sure the Project name was changed, make sure it is a new one project_name = self.form_data.get("name", None) if project_name: diff --git a/src/simmate/apps/project_management/components/tag_form.py b/src/simmate/apps/project_management/components/tag_form.py index bdda155a1..a94551236 100644 --- a/src/simmate/apps/project_management/components/tag_form.py +++ b/src/simmate/apps/project_management/components/tag_form.py @@ -1,52 +1,59 @@ # -*- coding: utf-8 -*- - -from simmate.website.core_components.components import DynamicFormComponent +from simmate.website.htmx.components import DynamicTableForm from ..models import Project, Tag -class TagFormView(DynamicFormComponent): +class TagForm(DynamicTableForm): - template_name = "projects/tag/form.html" table = Tag + template_name = "project_management/tag/form.html" + + # ------------------------------------------------------------------------- + + def mount_extra(self): + self.project_options = Project.project_options + + def update_tag_type(self): + if self.form_data.get("tag_type") == "project-specific": + self.js_actions = [ + {"refresh_select2": []}, + ] + # ------------------------------------------------------------------------- + # CREATE + required_inputs = [ "tag_type", "name", "description", ] - search_inputs = [ - "name", - "tag_type", - "project_id", - ] - class Meta: - javascript_exclude = ( - "tag_type_options", - "project_options", - *DynamicFormComponent.Meta.javascript_exclude, - ) + # ------------------------------------------------------------------------- - def mount_extra(self): - self.project_options = Project.project_options + # UPDATE # ------------------------------------------------------------------------- - tag_type = None - tag_type_options = [(o, o) for o in Tag.tag_type_options] + # CREATE MANY + # disabled - project_id = None - project_options = [] # set by mount() + # ------------------------------------------------------------------------- - name = None + # UPDATE MANY + # disabled - description = None + # ------------------------------------------------------------------------- + + # SEARCH - def set_tag_type(self, tag_type): - self.tag_type = tag_type - if self.tag_type == "project-specific": - self.call("refresh_select2") + search_inputs = [ + "name", + "tag_type", + "project_id", + ] + + # ------------------------------------------------------------------------- diff --git a/src/simmate/apps/project_management/components/token_balance_form.py b/src/simmate/apps/project_management/components/token_balance_form.py new file mode 100644 index 000000000..ca4626a9e --- /dev/null +++ b/src/simmate/apps/project_management/components/token_balance_form.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from django.http import HttpResponseRedirect + +from simmate.website.htmx.components import HtmxComponent + + +class TokenBalanceForm(HtmxComponent): + + template_name = "project_management/wallet/token_balance_form.html" + + def submit(self): + + wallet = self.request.user.wallet + + wallet.adjust_tokens( + token_amount=float(self.form_data["token_amount"]), + sending_user=self.request.user, + ) + + # just refresh the current page to get the updated balance + return HttpResponseRedirect(self.initial_context.request.path_info) diff --git a/src/simmate/apps/project_management/migrations/0001_initial.py b/src/simmate/apps/project_management/migrations/0001_initial.py index 3c086a0b9..5510d692c 100644 --- a/src/simmate/apps/project_management/migrations/0001_initial.py +++ b/src/simmate/apps/project_management/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.7 on 2025-12-14 02:52 +# Generated by Django 4.2.7 on 2026-01-01 23:09 import django.db.models.deletion from django.conf import settings diff --git a/src/simmate/apps/project_management/models/project.py b/src/simmate/apps/project_management/models/project.py index c118e679f..49bdca5b6 100644 --- a/src/simmate/apps/project_management/models/project.py +++ b/src/simmate/apps/project_management/models/project.py @@ -155,3 +155,38 @@ def html_extra_entry_context(self) -> dict: } # ------------------------------------------------------------------------- + + # This section is DEPRECIATED in favor of form mix-ins + + @property + def tag_options(self) -> list[tuple]: + + from .tag import Tag + + # query for all tags directly linked + project_tags = self.tags.order_by("name").values_list("id", "name").all() + + # add in generic tags after specific ones + generic_tags = ( + Tag.objects.filter(tag_type="all-projects") + .order_by("name") + .values_list("id", "name") + .all() + ) + + # reformat into tuple of (value, display) + return [ + (id, tag_name) for id, tag_name in list(project_tags) + list(generic_tags) + ] + + @classmethod + @property + def project_options(cls) -> list[tuple]: + + # query for all tags + projects = cls.objects.order_by("name").values_list("id", "name").all() + + # reformat into tuple of (value, display) + return [(id, name) for id, name in projects] + + # ------------------------------------------------------------------------- diff --git a/src/simmate/apps/project_management/models/tag.py b/src/simmate/apps/project_management/models/tag.py index 4971f51eb..fda11acf9 100644 --- a/src/simmate/apps/project_management/models/tag.py +++ b/src/simmate/apps/project_management/models/tag.py @@ -16,15 +16,15 @@ class Meta: "like hypotheses, targets, & orders. " ) - html_entries_template = "projects/tag/table.html" - html_entry_template = "projects/tag/view.html" - - # html_form_component = "tag-form" - # html_enabled_forms = [ - # "search", - # "create", - # "update", - # ] + html_entries_template = "project_management/tag/table.html" + html_entry_template = "project_management/tag/view.html" + + html_form_component = "tag-form" + html_enabled_forms = [ + "search", + "create", + "update", + ] # TODO: Maybe allow FilteredScope objects to be linked to these for # auto-tagging things in other tables diff --git a/src/simmate/apps/project_management/models/transaction.py b/src/simmate/apps/project_management/models/transaction.py index 4075a2d60..4466b5c79 100644 --- a/src/simmate/apps/project_management/models/transaction.py +++ b/src/simmate/apps/project_management/models/transaction.py @@ -15,6 +15,11 @@ class Meta: html_display_name = "Wallet Transactions" html_description_short = "The full ledger of wallet transactions" + html_entries_template = "project_management/transaction/table.html" + html_entry_template = "project_management/transaction/view.html" + + # ------------------------------------------------------------------------- + status_options = [ "Pending", "Complete", @@ -26,12 +31,12 @@ class Meta: status = table_column.CharField(max_length=30, blank=True, null=True) transaction_type_options = [ - "funding", - "transfer", - "payment", - "refund", - "penalty", - "admin_adjustment", + "Fund", + "Transfer", + "Payment", + "Refund", + "Penalty", + "Adjust", ] transaction_type = table_column.CharField(max_length=30, blank=True, null=True) @@ -67,8 +72,10 @@ class Meta: "Banned Account", "Other", ] - admin_adjustment_options = [ - "Issue Fix", + adjustment_options = [ + "Issue Fix (by admin)", + "Add Tokens", + "Remove Tokens", "Other", ] transaction_subtype = set( @@ -76,7 +83,7 @@ class Meta: + transfer_options + payment_options + penalty_options - + admin_adjustment_options + + adjustment_options ) transaction_subtype = table_column.CharField(max_length=30, blank=True, null=True) diff --git a/src/simmate/apps/project_management/models/wallet.py b/src/simmate/apps/project_management/models/wallet.py index 1eb786af8..27adbe468 100644 --- a/src/simmate/apps/project_management/models/wallet.py +++ b/src/simmate/apps/project_management/models/wallet.py @@ -1,6 +1,10 @@ # -*- coding: utf-8 -*- +from decimal import Decimal + from django.contrib.auth.models import User +from django.db import transaction as django_db_transaction +from django.db.models import F, Sum from simmate.database.base_data_types import DatabaseTable, table_column @@ -12,20 +16,28 @@ class Wallet(DatabaseTable): class Meta: db_table = "project_management__wallets" + # ------------------------------------------------------------------------- + html_display_name = "Wallets" html_description_short = ( "Digital accounts that contain USDC or Token assets for budgeting and spending." ) + html_entries_template = "project_management/wallet/table.html" + html_entry_template = "project_management/wallet/view.html" + + # ------------------------------------------------------------------------- + source = None # disable col wallet_type_options = [ "project", "user", # unique types where only one exists for each: - "simmate-escrow", "simmate-treasury", + "simmate-escrow", "validator-pool", + "simmate-bridge", # record keeping for on/off ramps ] wallet_type = table_column.CharField(max_length=30, blank=True, null=True) """ @@ -66,3 +78,224 @@ class Meta: decimal_places=6, default=0, ) + + # ------------------------------------------------------------------------- + + @classmethod + def _load_data(cls): + + # ensure default wallets exist + for wallet in [ + "simmate-treasury", + "simmate-escrow", + "validator-pool", + "simmate-bridge", + ]: + cls.objects.get_or_create(wallet_type=wallet) + if cls.objects.filter(wallet_type=wallet).count() != 1: + raise Exception(f"More than one entry for wallet type {wallet}") + + # check ethereum blockchain for new USDC transactions + # TO-DO + + # ------------------------------------------------------------------------- + + @classmethod + def validate_ledger(cls, usdc: bool = True, tokens: bool = True): + + if usdc: + total_usdc = cls.objects.aggregate( + total_usdc=Sum(F("usdc_balance") + F("collateral_balance")) + )["total_usdc"] + assert total_usdc == 0 + + if tokens: + total_tokens = cls.objects.aggregate(total_tokens=Sum("token_balance"))[ + "total_tokens" + ] + assert total_tokens == 0 + + # TODO: check transaction history for individual wallets and make + # sure each sums up to the wallet's current balances + + # ------------------------------------------------------------------------- + + def send( + self, # from_wallet + to_wallet, + usdc_amount: float = 0, + token_amount: float = 0, + # + status: str = None, + transaction_type: str = None, + transaction_subtype: str = None, + sending_user: User = None, + comments: str = None, + ): + + from .transaction import Transaction + + with django_db_transaction.atomic(): + + if usdc_amount: + usdc_amount = Decimal(usdc_amount) + if token_amount: + token_amount = Decimal(token_amount) + + if self.id == to_wallet.id: + raise Exception("to and from wallets can not be the same") + + if not usdc_amount and not token_amount: + raise Exception("At least one amount (USDC or Token) must be given") + + if (usdc_amount and usdc_amount <= 0) or ( + token_amount and token_amount <= 0 + ): + raise Exception("Amounts must be greater than 0") + + if usdc_amount: + # only the treasury & bridge are allowed to have a negative USDC balance + if ( + self.wallet_type not in ["simmate-bridge", "simmate-treasury"] + and self.usdc_balance < usdc_amount + ): + raise Exception("Insufficient USDC balance") + to_wallet.usdc_balance += usdc_amount + self.usdc_balance -= usdc_amount + + if token_amount: + # only the bridge can have a negative token balance + if ( + self.wallet_type not in ["simmate-bridge"] + and self.token_balance < token_amount + ): + raise Exception("Insufficient Token balance") + + # block unrealistic balances (>$1 billion) + expected_amt = to_wallet.token_balance + token_amount + if expected_amt > 1_000_000_000: + raise Exception("Unrealistic Token Balance (>$1 billion)") + + to_wallet.token_balance += token_amount + self.token_balance -= token_amount + + self.save() + to_wallet.save() + + transaction = Transaction( + from_wallet=self, + to_wallet=to_wallet, + # + usdc_amount=usdc_amount, + token_amount=token_amount, + # + status=status, + transaction_type=transaction_type, + transaction_subtype=transaction_subtype, + sending_user=sending_user, + comments=comments, + ) + transaction.save() + + self.validate_ledger() + + def stake( + self, # from_wallet AND to_wallet + amount: float, + sending_user: User, + comments: str, + ): + # a unique internal transaction where funds are moved within the same wallet + + from .transaction import Transaction + + amount = Decimal(amount) + + if amount > 0 and self.usdc_balance < amount: + raise Exception("Insufficient USDC balance") + elif amount < 0 and self.collateral_balance < amount: + raise Exception("Insufficient Collateral balance") + else: + raise Exception("Staking amount must be non-zero") + + with django_db_transaction.atomic(): + + self.usdc_balance -= amount + self.collateral_balance += amount + self.save() + + transaction = Transaction( + from_wallet=self, + to_wallet=self, + usdc_amount=amount, + collateral_amount=amount * -1, + status="Complete", # no delay/review needed + transaction_type="Transfer", + transaction_subtype=( + "Add Collateral" if amount > 0 else "Remove Collateral" + ), + sending_user=sending_user, + comments=comments, + ) + transaction.save() + + self.validate_ledger() + + # ------------------------------------------------------------------------- + + # utils to help with common transaction types + populating metadata + + def send_promotion( + self, + usdc_amount: float, + sending_user: User, + comments: str = None, + ): + + from_wallet = self.__class__.objects.get(wallet_type="simmate-treasury") + + assert sending_user.is_superuser + + from_wallet.send( + to_wallet=self, + usdc_amount=usdc_amount, + # + status="Complete", # no delay/review needed + transaction_type="Fund", + transaction_subtype="Promotion", + sending_user=sending_user, + comments=comments, + ) + + def adjust_tokens( + self, + token_amount: float, + sending_user: User, + comments: str = None, + ): + + bridge_wallet = self.__class__.objects.get(wallet_type="simmate-bridge") + + if token_amount < 0: + # flip the transaction so that it is a positive amount transfered + from_wallet = self + to_wallet = bridge_wallet + token_amount = abs(token_amount) + transaction_subtype = "Remove Tokens" + else: + from_wallet = bridge_wallet + to_wallet = self + transaction_subtype = "Add Tokens" + + from_wallet.send( + to_wallet=to_wallet, + token_amount=token_amount, + # + status="Complete", # no delay/review needed + transaction_type="Adjust", + transaction_subtype=transaction_subtype, + sending_user=sending_user, + comments=comments, + ) + + # ------------------------------------------------------------------------- diff --git a/src/simmate/apps/project_management/signals.py b/src/simmate/apps/project_management/signals.py new file mode 100644 index 000000000..754fd659d --- /dev/null +++ b/src/simmate/apps/project_management/signals.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +from django.contrib.auth import get_user_model +from django.db.models.signals import post_save +from django.dispatch import receiver + +from .models import Wallet + +User = get_user_model() + + +@receiver(post_save, sender=User) +def create_user_wallet(sender, instance, created, **kwargs): + if created: + Wallet.objects.get_or_create(user=instance, wallet_type="user") diff --git a/src/simmate/apps/project_management/templates/project_management/project/for_view/overview_tab.html b/src/simmate/apps/project_management/templates/project_management/project/for_view/overview_tab.html index b31723ad9..31d8d4174 100644 --- a/src/simmate/apps/project_management/templates/project_management/project/for_view/overview_tab.html +++ b/src/simmate/apps/project_management/templates/project_management/project/for_view/overview_tab.html @@ -14,20 +14,20 @@ Current Status - {% if table_entry.status == "PR" %} -

Pending Review

- {% elif table_entry.status == "A" %} + {% if table_entry.status == "Under Review" %} +

Under Review

+ {% elif table_entry.status == "Active" %}

Active

- {% elif table_entry.status == "RU" %} + {% elif table_entry.status == "Requires Update" %}

Requires Update

- {% elif table_entry.status == "IA" %} + {% elif table_entry.status == "Inactive" %}

Inactive

{% endif %} Discipline - {{ table_entry.get_discipline_display }} + {{ table_entry.discipline }} Leaders @@ -66,8 +66,10 @@
- Edit + +   Update +
diff --git a/src/simmate/apps/project_management/templates/project_management/project/for_view/tags_tab.html b/src/simmate/apps/project_management/templates/project_management/project/for_view/tags_tab.html index b16ba8c21..bff57b5c5 100644 --- a/src/simmate/apps/project_management/templates/project_management/project/for_view/tags_tab.html +++ b/src/simmate/apps/project_management/templates/project_management/project/for_view/tags_tab.html @@ -1,19 +1,19 @@
{% if tags__count %} - {% include "projects/tag/table.html" with entries=tags project_view=True truncated=tags__truncated %} + {% include "project_management/tag/table.html" with entries=tags project_view=True truncated=tags__truncated %} {% else %} No tags linked. {% endif %}
Add New + href="{% url 'data_explorer:table-entry-new' 'Tag' %}?tag_type=%22project-specific%22&project_id={{ table_entry.id }}">  Add View All + href="{% url 'data_explorer:table' 'Tag' %}?project_id={{ table_entry.id }}">  View All
diff --git a/src/simmate/apps/project_management/templates/project_management/project/form.html b/src/simmate/apps/project_management/templates/project_management/project/form.html index f77037c2e..47244a42b 100644 --- a/src/simmate/apps/project_management/templates/project_management/project/form.html +++ b/src/simmate/apps/project_management/templates/project_management/project/form.html @@ -26,10 +26,10 @@

{% endif %}
- {% htmx_selectbox name="leader_ids" label="Leaders" options=component.user_options multiselect=True %} + {% htmx_selectbox name="leaders__ids" label="Leaders" options=component.user_options multiselect=True %}
- {% htmx_selectbox name="member_ids" label="Members" options=component.user_options multiselect=True %} + {% htmx_selectbox name="members__ids" label="Members" options=component.user_options multiselect=True %}
diff --git a/src/simmate/apps/project_management/templates/project_management/project/table.html b/src/simmate/apps/project_management/templates/project_management/project/table.html index 3e705f11b..463dc9a50 100644 --- a/src/simmate/apps/project_management/templates/project_management/project/table.html +++ b/src/simmate/apps/project_management/templates/project_management/project/table.html @@ -1,8 +1,7 @@ {% extends "data_explorer/table_entries_base.html" %} {% block table_headers %} - {% table_header "id" "ID" min_width=60 %} - {% table_header "name" "Project" min_width=125 %} {% table_header "discipline" min_width=125 %} + {% table_header "name" "Project" min_width=125 %} {% table_header "status" min_width=150 %} Leader(s) Members (#) @@ -11,11 +10,8 @@ {% table_header "updated_at" "Updated" min_width=115 %} {% endblock %} {% block table_rows %} - {% foreign_key_link entry %} - - {{ entry.name }} - {{ entry.discipline }} + {% foreign_key_link entry "name" %} {% if entry.status == "Under Review" %} {% status_bar "Under Review" "secondary" %} @@ -38,7 +34,7 @@ {% endfor %} {{ entry.members.count }} - {{ entry.description }} + {{ entry.description|truncatechars:40 }} {{ entry.created_at|date:"Y-m-d" }} {{ entry.updated_at|date:"Y-m-d" }} {% endblock %} diff --git a/src/simmate/apps/project_management/templates/project_management/project_management_form.html b/src/simmate/apps/project_management/templates/project_management/project_management_form.html index 538b60788..ea0bcd4bb 100644 --- a/src/simmate/apps/project_management/templates/project_management/project_management_form.html +++ b/src/simmate/apps/project_management/templates/project_management/project_management_form.html @@ -1,13 +1,15 @@
-
{% selectbox name="project_id" options=project_options %}
- {% if hypothesis_options %} -
{% selectbox name="hypothesis_id" options=hypothesis_options %}
+
+ {% htmx_selectbox name="project_id" options=component.project_options defer=False include=include %} +
+ {% if component.hypothesis_options %} +
{% htmx_selectbox name="hypothesis_id" options=component.hypothesis_options %}
{% endif %}
-{% if tag_options %} +{% if component.tag_options %}
- {% selectbox name="tag_ids" label="Tags" options=tag_options dynamic_options=True multiselect=True %} + {% htmx_selectbox name="tags__ids" label="Tags" options=component.tag_options dynamic_options=True multiselect=True %}
{% endif %} diff --git a/src/simmate/apps/project_management/templates/project_management/project_management_form_many.html b/src/simmate/apps/project_management/templates/project_management/project_management_form_many.html index 74abe9384..88a0d9ae0 100644 --- a/src/simmate/apps/project_management/templates/project_management/project_management_form_many.html +++ b/src/simmate/apps/project_management/templates/project_management/project_management_form_many.html @@ -1,12 +1,12 @@ -{% selectbox name="project_id" options=project_options show_label=False %} +{% htmx_selectbox name="project_id" options=component.project_options show_label=False defer=False %} {# bug: need the id because selectbox isn't going away for some reason... #} - - {% if hypothesis_options %} - {% selectbox name="hypothesis_id" options=hypothesis_options dynamic_options=True show_label=False %} + + {% if component.hypothesis_options %} + {% htmx_selectbox name="hypothesis_id" options=component.hypothesis_options dynamic_options=True show_label=False %} {% endif %} - - {% if tag_options %} - {% selectbox name="tag_ids" label="Tags" options=tag_options dynamic_options=True multiselect=True show_label=False %} + + {% if component.tag_options %} + {% htmx_selectbox name="tags__ids" label="Tags" options=component.tag_options dynamic_options=True multiselect=True show_label=False %} {% endif %} diff --git a/src/simmate/apps/project_management/templates/project_management/tag/form.html b/src/simmate/apps/project_management/templates/project_management/tag/form.html index 94689d2e0..c2967b002 100644 --- a/src/simmate/apps/project_management/templates/project_management/tag/form.html +++ b/src/simmate/apps/project_management/templates/project_management/tag/form.html @@ -1,25 +1,26 @@ -
+{% extends "htmx/form_base.html" %} +{% block form %} {% include "data_explorer/general_search_options.html" %}

1. Tag Info

-
{% selectbox name="tag_type" %}
- {% if tag_type == "project-specific" %} -
{% selectbox name="project_id" options=project_options %}
+
{% htmx_selectbox name="tag_type" method_name="update_tag_type" %}
+ {% if component.form_data.tag_type == "project-specific" or component.table_entry and component.table_entry.project_id %} +
{% htmx_selectbox name="project_id" options=component.project_options %}
{% endif %}
-
{% text_input name="name" placeholder="ex: Ring A" max_length="30" %}
+
{% htmx_text_input name="name" placeholder="ex: Ring A" max_length="30" %}
- {% if form_mode != "search" %} + {% if component.form_mode != "search" %}
- {% text_area name="description" placeholder="1-2 sentences describing the goal and/or core idea" %} + {% htmx_text_area name="description" placeholder="1-2 sentences describing the goal and/or core idea" %}
{% endif %}
{% include "data_explorer/dynamic_submit_button.html" %} -
+{% endblock %} diff --git a/src/simmate/apps/project_management/templates/project_management/tag/table.html b/src/simmate/apps/project_management/templates/project_management/tag/table.html index 0784965c6..0728e8a01 100644 --- a/src/simmate/apps/project_management/templates/project_management/tag/table.html +++ b/src/simmate/apps/project_management/templates/project_management/tag/table.html @@ -1,14 +1,14 @@ {% extends "data_explorer/table_entries_base.html" %} {% block table_headers %} - {% table_header "id" min_width=50 %} + {% table_header "id" "ID" min_width=60 %} {% table_header "name" "Tag" min_width=100 %} {% if not project_view %} - {% table_header "tag_type" min_width=100 %} + {% table_header "tag_type" min_width=150 %} {% table_header "project__name" "Project" min_width=100 %} {% endif %} {% table_header "description" min_width=300 %} - {% table_header "created_at" min_width=100 %} - {% table_header "updated_at" min_width=100 %} + {% table_header "created_at" "Created" min_width=115 %} + {% table_header "updated_at" "Updated" min_width=115 %} Edit {% endblock %} {% block table_rows %} diff --git a/src/simmate/apps/project_management/templates/project_management/transaction/table.html b/src/simmate/apps/project_management/templates/project_management/transaction/table.html new file mode 100644 index 000000000..2c6535f4a --- /dev/null +++ b/src/simmate/apps/project_management/templates/project_management/transaction/table.html @@ -0,0 +1,41 @@ +{% extends "data_explorer/table_entries_base.html" %} +{% block table_headers %} + {% table_header "id" "ID" min_width=60 %} + {% table_header "status" min_width=100 %} + {% table_header "transaction_type" "Type" min_width=100 %} + {% table_header "transaction_subtype" "Subtype" min_width=100 %} + {% table_header "sending_user__username" "User" min_width=100 %} + {% table_header "from_wallet__id" "From" min_width=100 %} + {% table_header "to_wallet__id" "To" min_width=100 %} + {% table_header "usdc_amount" "USDC" min_width=100 %} + {% table_header "token_amount" "Tokens" min_width=100 %} + {% table_header "collateral_amount" "USDC (collateral)" min_width=165 %} + {% table_header "created_at" "Date" min_width=115 %} +{% endblock %} +{% block table_rows %} + {% foreign_key_link entry %} + + {% if entry.status == "Pending" %} + {% status_bar "Pending" "secondary" %} + {% elif entry.status == "Complete" %} + {% status_bar "Complete" "success" %} + {% elif entry.status == "Canceled" %} + {% status_bar "Canceled" "danger" %} + {% elif entry.status == "Denied" %} + {% status_bar "Denied" "danger" %} + {% elif entry.status == "Failed" %} + {% status_bar "Failed" "danger" %} + {% elif entry.status == "Under Review" %} + {% status_bar "Under Review" "warning" %} + {% endif %} + + {{ entry.transaction_type }} + {{ entry.transaction_subtype }} + {{ entry.sending_user.username }} + {% foreign_key_link entry.from_wallet %} + {% foreign_key_link entry.to_wallet %} + {{ entry.usdc_amount|floatformat:2|intcomma }} + {{ entry.token_amount|floatformat:0|intcomma }} + {{ entry.collateral_amount|floatformat:2|intcomma }} + {{ entry.created_at|date:"Y-m-d" }} +{% endblock %} diff --git a/src/simmate/apps/project_management/templates/project_management/transaction/view.html b/src/simmate/apps/project_management/templates/project_management/transaction/view.html new file mode 100644 index 000000000..c98f68a2a --- /dev/null +++ b/src/simmate/apps/project_management/templates/project_management/transaction/view.html @@ -0,0 +1,2 @@ +{% extends "data_explorer/table_entry.html" %} +{% block entrycontent %}to-do{% endblock %} diff --git a/src/simmate/apps/project_management/templates/project_management/wallet/table.html b/src/simmate/apps/project_management/templates/project_management/wallet/table.html new file mode 100644 index 000000000..4b5f6fcd7 --- /dev/null +++ b/src/simmate/apps/project_management/templates/project_management/wallet/table.html @@ -0,0 +1,31 @@ +{% extends "data_explorer/table_entries_base.html" %} +{% block table_headers %} + {% table_header "id" "ID" min_width=60 %} + {% table_header "wallet_type" min_width=100 %} + {% table_header "user__username" "User" min_width=100 %} + {% table_header "project__name" "Project" min_width=100 %} + {% table_header "usdc_balance" "USDC" min_width=100 %} + {% table_header "token_balance" "Tokens" min_width=100 %} + {% table_header "collateral_balance" "USDC (collateral)" min_width=100 %} +{% endblock %} +{% block table_rows %} + {% foreign_key_link entry %} + {{ entry.wallet_type }} + + {% if entry.user %} + {{ entry.user.username }} + {% else %} + --- + {% endif %} + + + {% if entry.project %} + {{ entry.project.name }} + {% else %} + --- + {% endif %} + + {{ entry.usdc_balance|floatformat:2|intcomma }} + {{ entry.token_balance|floatformat:0|intcomma }} + {{ entry.collateral_balance|floatformat:2|intcomma }} +{% endblock %} diff --git a/src/simmate/apps/project_management/templates/project_management/wallet/token_balance_form.html b/src/simmate/apps/project_management/templates/project_management/wallet/token_balance_form.html new file mode 100644 index 000000000..322209d68 --- /dev/null +++ b/src/simmate/apps/project_management/templates/project_management/wallet/token_balance_form.html @@ -0,0 +1,13 @@ +{% extends "htmx/form_base.html" %} +{% block form %} + + +{% endblock %} diff --git a/src/simmate/apps/project_management/templates/project_management/wallet/view.html b/src/simmate/apps/project_management/templates/project_management/wallet/view.html new file mode 100644 index 000000000..c98f68a2a --- /dev/null +++ b/src/simmate/apps/project_management/templates/project_management/wallet/view.html @@ -0,0 +1,2 @@ +{% extends "data_explorer/table_entry.html" %} +{% block entrycontent %}to-do{% endblock %} diff --git a/src/simmate/website/core_components/templates/core_components/navbar.html b/src/simmate/website/core_components/templates/core_components/navbar.html index 025189718..f0649265d 100644 --- a/src/simmate/website/core_components/templates/core_components/navbar.html +++ b/src/simmate/website/core_components/templates/core_components/navbar.html @@ -103,7 +103,7 @@

data-bs-toggle="modal" data-bs-target="#usdcModal"> - 5.00 + {{ user.wallet.usdc_balance|floatformat:2|intcomma }} {# Simmate Token button #} {# user name + profile button #} @@ -202,24 +202,7 @@

simmate.eth

-
- {% csrf_token %} - - -
+ {% htmx_component "token-balance-form" %} diff --git a/src/simmate/website/data_explorer/templates/data_explorer/table_entries_base.html b/src/simmate/website/data_explorer/templates/data_explorer/table_entries_base.html index a2d471296..5502c061c 100644 --- a/src/simmate/website/data_explorer/templates/data_explorer/table_entries_base.html +++ b/src/simmate/website/data_explorer/templates/data_explorer/table_entries_base.html @@ -1,27 +1,29 @@
{% block table_report %}{% endblock %}
-
-