diff --git a/BackEnd/profiles/serializers.py b/BackEnd/profiles/serializers.py index 1f2e3aa75..078edaf14 100644 --- a/BackEnd/profiles/serializers.py +++ b/BackEnd/profiles/serializers.py @@ -56,6 +56,18 @@ def to_internal_value(self, data): return ProfileImage.objects.filter(uuid=data, is_deleted=False).first() +class ProfileImageFieldApprovedStatus(ProfileImageField): + def to_representation(self, value): + if not value.is_deleted: + return { + "uuid": value.uuid, + "path": self.context["request"].build_absolute_uri( + value.image_path.url + ), + "is_approved": value.is_approved, + } + + class ProfileListSerializer(serializers.ModelSerializer): activities = ActivitySerializer(many=True, read_only=True) categories = CategorySerializer(many=True, read_only=True) @@ -200,8 +212,8 @@ class ProfileOwnerDetailViewSerializer(serializers.ModelSerializer): email = serializers.ReadOnlyField(source="person.email") regions = RegionSerializer(many=True, read_only=True) regions_ukr_display = serializers.SerializerMethodField() - banner = ProfileImageField() - logo = ProfileImageField() + banner = ProfileImageFieldApprovedStatus() + logo = ProfileImageFieldApprovedStatus() class Meta: model = Profile @@ -231,6 +243,8 @@ class Meta: "banner", "logo", "is_deleted", + "status", + "status_updated_at", ) read_only_fields = ( "id", @@ -258,6 +272,8 @@ class Meta: "banner", "logo", "is_deleted", + "status", + "status_updated_at", ) def get_regions_ukr_display(self, obj) -> str: diff --git a/FrontEnd/public/img/moderation-icon.png b/FrontEnd/public/img/moderation-icon.png new file mode 100644 index 000000000..c2187312d Binary files /dev/null and b/FrontEnd/public/img/moderation-icon.png differ diff --git a/FrontEnd/src/components/MiniComponents/PendingModerationIcon/PendingStatus.jsx b/FrontEnd/src/components/MiniComponents/PendingModerationIcon/PendingStatus.jsx new file mode 100644 index 000000000..3550ad515 --- /dev/null +++ b/FrontEnd/src/components/MiniComponents/PendingModerationIcon/PendingStatus.jsx @@ -0,0 +1,40 @@ +import { Tooltip } from 'antd'; +import styles from './PendingStatus.module.css'; + +const PendingStatus = ({ profile, elementType }) => { + + const bannerApproved = profile?.banner.is_approved; + const logoApproved = profile?.logo.is_approved; + + const shouldShowTooltip = (elementType === 'banner' && bannerApproved === false) + || (elementType === 'logo' && logoApproved === false); + + const formattedDate = new Date(profile?.status_updated_at).toLocaleString('uk-UA', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + }); + + const tooltipText = `Статус модерації: Очікується. Час відправки запиту: ${formattedDate}`; + + return ( + (profile?.status === 'pending' && shouldShowTooltip) ? ( +
+ + Pending status icon + +
+ ) : null + ); +}; + +export default PendingStatus; diff --git a/FrontEnd/src/components/MiniComponents/PendingModerationIcon/PendingStatus.module.css b/FrontEnd/src/components/MiniComponents/PendingModerationIcon/PendingStatus.module.css new file mode 100644 index 000000000..2c4bc5e06 --- /dev/null +++ b/FrontEnd/src/components/MiniComponents/PendingModerationIcon/PendingStatus.module.css @@ -0,0 +1,7 @@ +.tooltip-container{ + display: flex; + position:absolute; + padding-top: 5px; + padding-left: 3px; + } + \ No newline at end of file diff --git a/FrontEnd/src/components/ProfileDetail/ProfileDetailPage.jsx b/FrontEnd/src/components/ProfileDetail/ProfileDetailPage.jsx index adfa78170..0ffe54a55 100644 --- a/FrontEnd/src/components/ProfileDetail/ProfileDetailPage.jsx +++ b/FrontEnd/src/components/ProfileDetail/ProfileDetailPage.jsx @@ -11,6 +11,7 @@ import DetailedInfoSection from './DetailedInfo/DetailedInfoSection'; import BannerImage from './BannerImage'; import { ActiveLinksContext } from '../../context/ActiveLinksContext'; import classes from './ProfileDetailPage.module.css'; +import PendingStatus from '../MiniComponents/PendingModerationIcon/PendingStatus'; function ProfileDetailPage({ isAuthorized }) { const [activeLinks, setActiveLinks] = useState([]); @@ -31,7 +32,7 @@ function ProfileDetailPage({ isAuthorized }) { const notRequiredData = ['address', 'banner', 'logo', 'common_info', 'edrpou', 'rnokpp', 'founded', 'official_name', 'product_info', 'service_info', 'startup_idea', 'logistics', 'cooperation']; const containsNotRequiredData = fetchedProfile ? Object.keys(fetchedProfile).some(key => notRequiredData.includes(key) && fetchedProfile[key] !== '' && fetchedProfile[key] !== null) : false; - return (error && error.response.status !== 401) ? ( + return (error && error.status !== 401) ? ( ) : (
@@ -39,6 +40,9 @@ function ProfileDetailPage({ isAuthorized }) { ) : ( +
+ +
+
+ +
{ + const backgroundImage = { background: `url(${value}) lightgray 50% / cover no-repeat`, }; @@ -56,11 +59,18 @@ const ImageField = ({ ); return ( -
+
-