diff --git a/backend/clubs/serializers.py b/backend/clubs/serializers.py index 015a4c7da..c477a9c8f 100644 --- a/backend/clubs/serializers.py +++ b/backend/clubs/serializers.py @@ -2974,8 +2974,7 @@ class Meta: ) -class AdminNoteSerializer(serializers.ModelSerializer): - club = serializers.SlugRelatedField(queryset=Club.objects.all(), slug_field="code") +class AdminNoteSerializer(ClubRouteMixin, serializers.ModelSerializer): creator = serializers.SerializerMethodField("get_creator") title = serializers.CharField(max_length=255, default="Note") content = serializers.CharField(required=False) @@ -2984,16 +2983,16 @@ def get_creator(self, obj): return obj.creator.get_full_name() def create(self, validated_data): - return AdminNote.objects.create( - creator=self.context["request"].user, - club=validated_data["club"], - title=validated_data["title"], - content=validated_data["content"], - ) + validated_data["creator"] = self.context["request"].user + return super().create(validated_data) + + def update(self, instance, validated_data): + validated_data.pop("creator", "") + return super().update(instance, validated_data) class Meta: model = AdminNote - fields = ("id", "creator", "club", "title", "content", "created_at") + fields = ("id", "creator", "title", "content", "created_at") class WritableClubFairSerializer(ClubFairSerializer): diff --git a/backend/clubs/views.py b/backend/clubs/views.py index a5f7fa6fc..f7a93f6d4 100644 --- a/backend/clubs/views.py +++ b/backend/clubs/views.py @@ -7382,7 +7382,9 @@ class AdminNoteViewSet(viewsets.ModelViewSet): http_method_names = ["get", "post", "put", "patch", "delete"] def get_queryset(self): - return AdminNote.objects.filter(club__code=self.kwargs.get("club_code")) + return AdminNote.objects.filter( + club__code=self.kwargs.get("club_code") + ).order_by("-created_at") class ScriptExecutionView(APIView): diff --git a/frontend/components/ClubEditPage.tsx b/frontend/components/ClubEditPage.tsx index 462fa002f..72445905e 100644 --- a/frontend/components/ClubEditPage.tsx +++ b/frontend/components/ClubEditPage.tsx @@ -26,6 +26,7 @@ import { School, StudentType, Tag, + UserInfo, VisitType, Year, } from '../types' @@ -42,6 +43,7 @@ import { SHOW_ORG_MANAGEMENT, SITE_NAME, } from '../utils/branding' +import AdminNoteCard from './ClubEditPage/AdminNoteCard' import AdvisorCard from './ClubEditPage/AdvisorCard' import AnalyticsCard from './ClubEditPage/AnalyticsCard' import ApplicationsCard from './ClubEditPage/ApplicationsCard' @@ -74,6 +76,7 @@ type ClubFormProps = { tags: Tag[] studentTypes: StudentType[] tab?: string | null + userInfo?: UserInfo } const ClubForm = ({ @@ -85,6 +88,7 @@ const ClubForm = ({ studentTypes, clubId, tab, + userInfo, }: ClubFormProps): ReactElement => { const [club, setClub] = useState(null) const [isEdit, setIsEdit] = useState(typeof clubId !== 'undefined') @@ -237,6 +241,15 @@ const ClubForm = ({ /> ), }, + ...(userInfo !== undefined && userInfo.is_superuser + ? [ + { + name: 'notes', + label: 'Administrator Notes', + content: , + }, + ] + : []), { name: 'member', label: OBJECT_TAB_MEMBERSHIP_LABEL, diff --git a/frontend/components/ClubEditPage/AdminNoteCard.tsx b/frontend/components/ClubEditPage/AdminNoteCard.tsx new file mode 100644 index 000000000..810eba016 --- /dev/null +++ b/frontend/components/ClubEditPage/AdminNoteCard.tsx @@ -0,0 +1,46 @@ +import { Field } from 'formik' +import moment from 'moment-timezone' +import { ReactElement } from 'react' + +import { Club } from '../../types' +import { TextField } from '../FormComponents' +import { ModelForm } from '../ModelForm' +import BaseCard from './BaseCard' + +type AdminNoteCardProps = { + club: Club +} + +export default function AdminNoteCard({ + club, +}: AdminNoteCardProps): ReactElement { + const noteTableFields = [ + { label: 'Author', name: 'creator' }, + { label: 'Note', name: 'content' }, + { + label: 'Created On', + name: 'created_at', + converter: (field) => moment(field).format('MMMM Do, YYYY'), + }, + ] + + return ( + +

+ Below is a list of notes about {club.name}. These notes are only visible + to site administrators. +

+ + + + } + tableFields={noteTableFields} + searchableColumns={['content']} + noun="Note" + /> +
+ ) +}