Merge pull request 'production' (#6) from production into main
Reviewed-on: #6
This commit is contained in:
commit
3d91d41487
@ -56,11 +56,15 @@ async def send_delivery(
|
||||
continue
|
||||
|
||||
# Уведомление получателю
|
||||
if (exhibition:=existing.get('exhibition',{})) and (exhibition_id:=existing.get('exhibition_id')):
|
||||
link = f'/profile/delivery/exhibition/{exhibition_id}'
|
||||
else:
|
||||
link = f'/profile/delivery'
|
||||
notify = await notify_in_profile(NotifyItem(
|
||||
user_id=str(existing["receiver"]["_id"]),
|
||||
type_item="delivery",
|
||||
title="Доставка картин",
|
||||
link="/profile/delivery",
|
||||
link=link,
|
||||
text=f"{str(existing['sender']['username'])} — прислал вам картины."
|
||||
))
|
||||
|
||||
@ -106,13 +110,16 @@ async def accept_delivery(
|
||||
if not existing:
|
||||
errors.append({"item": item_id, "error": "Доставка не найдена или не для вас"})
|
||||
continue
|
||||
|
||||
if (exhibition:=existing.get('exhibition',{})) and (exhibition_id:=existing.get('exhibition_id')):
|
||||
link = f'/profile/delivery/exhibition/{exhibition_id}'
|
||||
else:
|
||||
link = f'/profile/delivery'
|
||||
# Уведомление отправителю
|
||||
notify = await notify_in_profile(NotifyItem(
|
||||
user_id=str(existing["sender"]["_id"]),
|
||||
type_item="delivery",
|
||||
title="Доставка картин",
|
||||
link="/profile/delivery",
|
||||
link=link,
|
||||
text=f"Картина «{existing['title']}» получена — {str(existing['receiver']['username'])}."
|
||||
))
|
||||
|
||||
|
||||
116
main.ipynb
116
main.ipynb
@ -564,12 +564,21 @@
|
||||
" )\n",
|
||||
" if exhibition_finished:\n",
|
||||
" exhibition_id_str = str(exhibition_finished[\"_id\"])\n",
|
||||
" cursor = delivery_collection.aggregate([\n",
|
||||
" {\"$match\": {\n",
|
||||
" \"exhibition_id\": exhibition_id_str, **prod_filter\n",
|
||||
" }},\n",
|
||||
" {\"$sort\": {\"_id\": -1}},\n",
|
||||
" {\"$group\": {\n",
|
||||
" \"_id\": \"$post_id\",\n",
|
||||
" \"doc\": {\"$first\": \"$$ROOT\"}\n",
|
||||
" }},\n",
|
||||
" {\"$replaceRoot\": {\"newRoot\": \"$doc\"}},\n",
|
||||
" {\"$sort\": {\"_id\": -1}},\n",
|
||||
" ])\n",
|
||||
"\n",
|
||||
" async for doc in delivery_collection.find(\n",
|
||||
" {\"exhibition_id\": exhibition_id_str, \"status\": \"Получено\", **prod_filter},\n",
|
||||
" {\"_id\": 0},\n",
|
||||
" ):\n",
|
||||
" sender = doc.get(\"sender\")\n",
|
||||
" data = [doc async for doc in cursor if doc.get('status') == 'На выставке']\n",
|
||||
" for doc in data:\n",
|
||||
" post_id = doc.get(\"post_id\")\n",
|
||||
" exhibition_id = doc.get(\"exhibition_id\")\n",
|
||||
"\n",
|
||||
@ -578,9 +587,11 @@
|
||||
" \"delivery_type\": \"\",\n",
|
||||
" \"track\": \"\",\n",
|
||||
" \"status\": \"Возврат художнику\",\n",
|
||||
" \"receiver\": sender,\n",
|
||||
" \"sender\": doc.get(\"receiver\"),\n",
|
||||
" \"receiver\": doc.get(\"user\"),\n",
|
||||
" \"sender\": doc.get(\"exhibition\", {}).get(\"user\"),\n",
|
||||
" }\n",
|
||||
" new_doc.pop('_id')\n",
|
||||
" new_doc.pop('exhibition')\n",
|
||||
"\n",
|
||||
" await delivery_collection.update_one(\n",
|
||||
" {**prod_filter, \"status\": \"Возврат художнику\", \"post_id\": post_id, \"exhibition_id\": exhibition_id},\n",
|
||||
@ -1614,9 +1625,24 @@
|
||||
"):\n",
|
||||
" exhibition = await exhibition_collection.find_one({'_id': ObjectId(exhibition_id)})\n",
|
||||
" if exhibition:\n",
|
||||
" posts = await delivery_collection.find({'status': 'На выставке', \"exhibition_id\": exhibition_id}).to_list()\n",
|
||||
" match_ = {\n",
|
||||
" \"exhibition_id\": exhibition_id,\n",
|
||||
" 'prod': PROD,\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" cursor = delivery_collection.aggregate([\n",
|
||||
" {\"$match\": match_},\n",
|
||||
" {\"$sort\": {\"_id\": -1}},\n",
|
||||
" {\"$group\": {\n",
|
||||
" \"_id\": \"$post_id\",\n",
|
||||
" \"doc\": {\"$first\": \"$$ROOT\"}\n",
|
||||
" }},\n",
|
||||
" {\"$replaceRoot\": {\"newRoot\": \"$doc\"}},\n",
|
||||
" {\"$sort\": {\"_id\": -1}},\n",
|
||||
" ])\n",
|
||||
" posts = [doc async for doc in cursor if doc.get('status') == 'На выставке']\n",
|
||||
" return templates.TemplateResponse(\n",
|
||||
" request=request, name=\"exhibition/index.html\", context={\"id\": 1, \"user\": user, \"exhibition\": exhibition, \"posts\": posts}\n",
|
||||
" request=request, name=\"exhibition/index.html\", context={\"id\": 1, \"user\": user, \"exhibition\": exhibition, \"posts\": posts, 'now': datetime.now()}\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" raise HTTPException(status_code=307, headers={\"Location\": '/competitions'}, detail=\"Конкурс не найден\")"
|
||||
@ -1735,7 +1761,7 @@
|
||||
" 'prod': PROD\n",
|
||||
" }).sort({'_id':-1}).limit(1000).to_list()\n",
|
||||
" return templates.TemplateResponse(\n",
|
||||
" request=request, name=\"exhibitions.html\", context={\"id\": 1, \"user\": user, \"exhibitions\": exhibitions}\n",
|
||||
" request=request, name=\"exhibitions.html\", context={\"id\": 1, \"user\": user, \"exhibitions\": exhibitions, 'now': datetime.now()}\n",
|
||||
" )"
|
||||
],
|
||||
"display_name": ".venv"
|
||||
@ -1817,11 +1843,7 @@
|
||||
" ]})\n",
|
||||
" \n",
|
||||
" if not user:\n",
|
||||
" # Безопасность: всегда возвращать одинаковое время ответа/структуру сообщения, если возможно\n",
|
||||
" # Но для удобства использования мы можем сказать \"не найден\".\n",
|
||||
" # Чтобы предотвратить перечисление, некоторые приложения всегда возвращают \"OK\", но отправляют письмо только если найдено.\n",
|
||||
" # Здесь мы придерживаемся вашей оригинальной логики, но помните о риске перечисления.\n",
|
||||
" raise HTTPException(status_code=404, detail=\"Пользователь с таким email не найден\")\n",
|
||||
" return {'ok': True}\n",
|
||||
" \n",
|
||||
" # 4. Генерация токена\n",
|
||||
" token = secrets.token_urlsafe(32)\n",
|
||||
@ -1845,13 +1867,13 @@
|
||||
" # 7. Асинхронная отправка email\n",
|
||||
" try:\n",
|
||||
" email_task.notify.delay(\n",
|
||||
" recipient=user['email'],\n",
|
||||
" context={\n",
|
||||
" to_email=user['email'],\n",
|
||||
" data={\n",
|
||||
" 'link': reset_link, \n",
|
||||
" 'email': user['email']\n",
|
||||
" },\n",
|
||||
" subject='Восстановление пароля',\n",
|
||||
" template_name='restore.html'\n",
|
||||
" template='restore.html'\n",
|
||||
" )\n",
|
||||
" except Exception as e:\n",
|
||||
" # Логирование ошибки\n",
|
||||
@ -3229,6 +3251,53 @@
|
||||
],
|
||||
"display_name": ".venv"
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "8feec4b3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"@app.put(\"/api/post/{post_id}/sale\", response_model=dict)\n",
|
||||
"async def sale_painting(\n",
|
||||
" post_id: str,\n",
|
||||
" current_user: Dict = Security(get_current_user)\n",
|
||||
" ):\n",
|
||||
" fin = [{'_id': ObjectId(post_id), 'user._id': current_user.get('_id')}]\n",
|
||||
" if 'partner' in current_user.get('roles', []):\n",
|
||||
" fin.append({'_id': ObjectId(post_id)})\n",
|
||||
" doc = await posts_collection.update_one({'$or': fin}, {'$set': {\"status\": \"Продано\"}})\n",
|
||||
" if doc.modified_count: \n",
|
||||
" existing = await delivery_collection.find_one(\n",
|
||||
" {\"post_id\": post_id, \"status\": \"На выставке\"},\n",
|
||||
" {\"_id\": 0}\n",
|
||||
" )\n",
|
||||
" if existing:\n",
|
||||
" update_data = {\n",
|
||||
" \"status\": \"Продано\",\n",
|
||||
" \"post\": {\"status\": \"Продано\"},\n",
|
||||
" }\n",
|
||||
" notify = await notify_in_profile(NotifyItem(\n",
|
||||
" user_id=str(existing[\"user\"][\"_id\"]),\n",
|
||||
" type_item=\"delivery\",\n",
|
||||
" title=\"Картина продана\",\n",
|
||||
" link=\"/profile/delivery\",\n",
|
||||
" text=f\"{str(existing.get('post',{}).get('title'))}\"\n",
|
||||
" ))\n",
|
||||
"\n",
|
||||
" new_doc = {**existing, **update_data, \"notify\": notify.upserted_id}\n",
|
||||
"\n",
|
||||
" result = await delivery_collection.insert_one(new_doc)\n",
|
||||
" else:\n",
|
||||
" doc = await posts_collection.update_one({'$or': fin}, {'$set': {\"status\": \"В каталоге\"}})\n",
|
||||
" await delivery_collection.delete_one(\n",
|
||||
" {\"post_id\": post_id, \"status\": \"Продано\"}\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" return {'ok': True}"
|
||||
],
|
||||
"display_name": ".venv"
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
@ -4420,7 +4489,7 @@
|
||||
" if field == 'name':\n",
|
||||
" field = 'title'\n",
|
||||
" if op == \"like\":\n",
|
||||
" return {field: {\"$regex\": value, \"$options\": \"i\"}}\n",
|
||||
" return {field: {\"$regex\": safe_strip(value), \"$options\": \"i\"}}\n",
|
||||
" return {field: value}\n",
|
||||
" mongo_query = mql_to_mongo(mql)\n",
|
||||
" for post in [doc async for doc in posts_collection.find(mongo_query).sort({\"_id\":-1}).limit(per_page * page + 8)][per_page * page - per_page:per_page * page]:\n",
|
||||
@ -4800,7 +4869,7 @@
|
||||
" ],\n",
|
||||
" 'prod': PROD,\n",
|
||||
" }},\n",
|
||||
" {\"$sort\": {\"_id\": -1}}, # why not work\n",
|
||||
" {\"$sort\": {\"_id\": -1}},\n",
|
||||
" {\"$group\": {\n",
|
||||
" \"_id\": \"$post_id\",\n",
|
||||
" \"doc\": {\"$first\": \"$$ROOT\"}\n",
|
||||
@ -4952,6 +5021,11 @@
|
||||
"\n",
|
||||
" galerists = [{\"_id\": uname, \"count\": counts_by_username.get(uname, '')} for uname in usernames]\n",
|
||||
" posts = [doc async for doc in posts_collection.find().sort('_id', -1).limit(32)]\n",
|
||||
" d_posts = await delivery_collection.find({'post_id':{'$in':[str(i.get('_id')) for i in posts]}}).to_list()\n",
|
||||
" for post in posts:\n",
|
||||
" for doc in d_posts:\n",
|
||||
" if str(post.get('_id')) == doc.get('post_id') and post.get('status') not in ['Продано', 'Приглашение отклонено'] and doc.get('status') not in ['Приглашение отклонено'] and doc.get('receiver') and doc.get('receiver').get('_id') == str(user.get('_id')):\n",
|
||||
" post.update({'status':doc.get('status'), 'receiver': doc.get('receiver'), 'd_id':doc.get('_id')})\n",
|
||||
" return templates.TemplateResponse(\n",
|
||||
" request=request, name=\"profile/partner/exhibition catalog.html\", context={\"now\": datetime.now(), \"user\": user, \"galerists\": galerists, \"genres\": genres, \"living_places\": living_places, \"exhibition\": exhibition, \"posts\": posts}\n",
|
||||
" )\n"
|
||||
@ -4977,7 +5051,7 @@
|
||||
" d_posts = await delivery_collection.find({'post_id':{'$in':[str(i.get('_id')) for i in posts]}}).to_list()\n",
|
||||
" for post in posts:\n",
|
||||
" for doc in d_posts:\n",
|
||||
" if str(post.get('_id')) == doc.get('post_id') and doc.get('receiver') and doc.get('receiver').get('_id') == str(user.get('_id')):\n",
|
||||
" if str(post.get('_id')) == doc.get('post_id') and post.get('status') not in ['Продано', 'Приглашение отклонено'] and doc.get('status') not in ['Приглашение отклонено'] and doc.get('receiver') and doc.get('receiver').get('_id') == str(user.get('_id')):\n",
|
||||
" post.update({'status':doc.get('status'), 'receiver': doc.get('receiver'), 'd_id':doc.get('_id')})\n",
|
||||
" return templates.TemplateResponse(\n",
|
||||
" request=request, name=\"profile/partner/exhibition.html\", context={\"now\": datetime.now(), \"user\": user, \"exhibition\": exhibition, \"posts\": posts}\n",
|
||||
|
||||
107
main.py
107
main.py
@ -352,12 +352,21 @@ async def cron_every_five_min(token: str = Security(get_token)):
|
||||
)
|
||||
if exhibition_finished:
|
||||
exhibition_id_str = str(exhibition_finished["_id"])
|
||||
cursor = delivery_collection.aggregate([
|
||||
{"$match": {
|
||||
"exhibition_id": exhibition_id_str, **prod_filter
|
||||
}},
|
||||
{"$sort": {"_id": -1}},
|
||||
{"$group": {
|
||||
"_id": "$post_id",
|
||||
"doc": {"$first": "$$ROOT"}
|
||||
}},
|
||||
{"$replaceRoot": {"newRoot": "$doc"}},
|
||||
{"$sort": {"_id": -1}},
|
||||
])
|
||||
|
||||
async for doc in delivery_collection.find(
|
||||
{"exhibition_id": exhibition_id_str, "status": "Получено", **prod_filter},
|
||||
{"_id": 0},
|
||||
):
|
||||
sender = doc.get("sender")
|
||||
data = [doc async for doc in cursor if doc.get('status') == 'На выставке']
|
||||
for doc in data:
|
||||
post_id = doc.get("post_id")
|
||||
exhibition_id = doc.get("exhibition_id")
|
||||
|
||||
@ -366,9 +375,11 @@ async def cron_every_five_min(token: str = Security(get_token)):
|
||||
"delivery_type": "",
|
||||
"track": "",
|
||||
"status": "Возврат художнику",
|
||||
"receiver": sender,
|
||||
"sender": doc.get("receiver"),
|
||||
"receiver": doc.get("user"),
|
||||
"sender": doc.get("exhibition", {}).get("user"),
|
||||
}
|
||||
new_doc.pop('_id')
|
||||
new_doc.pop('exhibition')
|
||||
|
||||
await delivery_collection.update_one(
|
||||
{**prod_filter, "status": "Возврат художнику", "post_id": post_id, "exhibition_id": exhibition_id},
|
||||
@ -1201,9 +1212,24 @@ async def exhibition_members(
|
||||
):
|
||||
exhibition = await exhibition_collection.find_one({'_id': ObjectId(exhibition_id)})
|
||||
if exhibition:
|
||||
posts = await delivery_collection.find({'status': 'На выставке', "exhibition_id": exhibition_id}).to_list()
|
||||
match_ = {
|
||||
"exhibition_id": exhibition_id,
|
||||
'prod': PROD,
|
||||
}
|
||||
|
||||
cursor = delivery_collection.aggregate([
|
||||
{"$match": match_},
|
||||
{"$sort": {"_id": -1}},
|
||||
{"$group": {
|
||||
"_id": "$post_id",
|
||||
"doc": {"$first": "$$ROOT"}
|
||||
}},
|
||||
{"$replaceRoot": {"newRoot": "$doc"}},
|
||||
{"$sort": {"_id": -1}},
|
||||
])
|
||||
posts = [doc async for doc in cursor if doc.get('status') == 'На выставке']
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="exhibition/index.html", context={"id": 1, "user": user, "exhibition": exhibition, "posts": posts}
|
||||
request=request, name="exhibition/index.html", context={"id": 1, "user": user, "exhibition": exhibition, "posts": posts, 'now': datetime.now()}
|
||||
)
|
||||
else:
|
||||
raise HTTPException(status_code=307, headers={"Location": '/competitions'}, detail="Конкурс не найден")
|
||||
@ -1286,7 +1312,7 @@ async def exhibitions_all(
|
||||
'prod': PROD
|
||||
}).sort({'_id':-1}).limit(1000).to_list()
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="exhibitions.html", context={"id": 1, "user": user, "exhibitions": exhibitions}
|
||||
request=request, name="exhibitions.html", context={"id": 1, "user": user, "exhibitions": exhibitions, 'now': datetime.now()}
|
||||
)
|
||||
|
||||
@app.get("/competitions", response_class=HTMLResponse)
|
||||
@ -1350,11 +1376,7 @@ async def restore_api(
|
||||
]})
|
||||
|
||||
if not user:
|
||||
# Безопасность: всегда возвращать одинаковое время ответа/структуру сообщения, если возможно
|
||||
# Но для удобства использования мы можем сказать "не найден".
|
||||
# Чтобы предотвратить перечисление, некоторые приложения всегда возвращают "OK", но отправляют письмо только если найдено.
|
||||
# Здесь мы придерживаемся вашей оригинальной логики, но помните о риске перечисления.
|
||||
raise HTTPException(status_code=404, detail="Пользователь с таким email не найден")
|
||||
return {'ok': True}
|
||||
|
||||
# 4. Генерация токена
|
||||
token = secrets.token_urlsafe(32)
|
||||
@ -1378,13 +1400,13 @@ async def restore_api(
|
||||
# 7. Асинхронная отправка email
|
||||
try:
|
||||
email_task.notify.delay(
|
||||
recipient=user['email'],
|
||||
context={
|
||||
to_email=user['email'],
|
||||
data={
|
||||
'link': reset_link,
|
||||
'email': user['email']
|
||||
},
|
||||
subject='Восстановление пароля',
|
||||
template_name='restore.html'
|
||||
template='restore.html'
|
||||
)
|
||||
except Exception as e:
|
||||
# Логирование ошибки
|
||||
@ -2402,6 +2424,44 @@ async def post_painting(
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.put("/api/post/{post_id}/sale", response_model=dict)
|
||||
async def sale_painting(
|
||||
post_id: str,
|
||||
current_user: Dict = Security(get_current_user)
|
||||
):
|
||||
fin = [{'_id': ObjectId(post_id), 'user._id': current_user.get('_id')}]
|
||||
if 'partner' in current_user.get('roles', []):
|
||||
fin.append({'_id': ObjectId(post_id)})
|
||||
doc = await posts_collection.update_one({'$or': fin}, {'$set': {"status": "Продано"}})
|
||||
if doc.modified_count:
|
||||
existing = await delivery_collection.find_one(
|
||||
{"post_id": post_id, "status": "На выставке"},
|
||||
{"_id": 0}
|
||||
)
|
||||
if existing:
|
||||
update_data = {
|
||||
"status": "Продано",
|
||||
"post": {"status": "Продано"},
|
||||
}
|
||||
notify = await notify_in_profile(NotifyItem(
|
||||
user_id=str(existing["user"]["_id"]),
|
||||
type_item="delivery",
|
||||
title="Картина продана",
|
||||
link="/profile/delivery",
|
||||
text=f"{str(existing.get('post',{}).get('title'))}"
|
||||
))
|
||||
|
||||
new_doc = {**existing, **update_data, "notify": notify.upserted_id}
|
||||
|
||||
result = await delivery_collection.insert_one(new_doc)
|
||||
else:
|
||||
doc = await posts_collection.update_one({'$or': fin}, {'$set': {"status": "В каталоге"}})
|
||||
await delivery_collection.delete_one(
|
||||
{"post_id": post_id, "status": "Продано"}
|
||||
)
|
||||
|
||||
return {'ok': True}
|
||||
|
||||
@app.put("/api/post/{post_id}", response_model=dict)
|
||||
async def put_painting(
|
||||
background_tasks: BackgroundTasks,
|
||||
@ -3389,7 +3449,7 @@ async def get_posts(
|
||||
if field == 'name':
|
||||
field = 'title'
|
||||
if op == "like":
|
||||
return {field: {"$regex": value, "$options": "i"}}
|
||||
return {field: {"$regex": safe_strip(value), "$options": "i"}}
|
||||
return {field: value}
|
||||
mongo_query = mql_to_mongo(mql)
|
||||
for post in [doc async for doc in posts_collection.find(mongo_query).sort({"_id":-1}).limit(per_page * page + 8)][per_page * page - per_page:per_page * page]:
|
||||
@ -3678,7 +3738,7 @@ async def profile_delivery(
|
||||
],
|
||||
'prod': PROD,
|
||||
}},
|
||||
{"$sort": {"_id": -1}}, # why not work
|
||||
{"$sort": {"_id": -1}},
|
||||
{"$group": {
|
||||
"_id": "$post_id",
|
||||
"doc": {"$first": "$$ROOT"}
|
||||
@ -3806,6 +3866,11 @@ async def profile_partner_exhibitions_catalog(
|
||||
|
||||
galerists = [{"_id": uname, "count": counts_by_username.get(uname, '')} for uname in usernames]
|
||||
posts = [doc async for doc in posts_collection.find().sort('_id', -1).limit(32)]
|
||||
d_posts = await delivery_collection.find({'post_id':{'$in':[str(i.get('_id')) for i in posts]}}).to_list()
|
||||
for post in posts:
|
||||
for doc in d_posts:
|
||||
if str(post.get('_id')) == doc.get('post_id') and post.get('status') not in ['Продано', 'Приглашение отклонено'] and doc.get('status') not in ['Приглашение отклонено'] and doc.get('receiver') and doc.get('receiver').get('_id') == str(user.get('_id')):
|
||||
post.update({'status':doc.get('status'), 'receiver': doc.get('receiver'), 'd_id':doc.get('_id')})
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="profile/partner/exhibition catalog.html", context={"now": datetime.now(), "user": user, "galerists": galerists, "genres": genres, "living_places": living_places, "exhibition": exhibition, "posts": posts}
|
||||
)
|
||||
@ -3823,7 +3888,7 @@ async def profile_partner_exhibitions(
|
||||
d_posts = await delivery_collection.find({'post_id':{'$in':[str(i.get('_id')) for i in posts]}}).to_list()
|
||||
for post in posts:
|
||||
for doc in d_posts:
|
||||
if str(post.get('_id')) == doc.get('post_id') and doc.get('receiver') and doc.get('receiver').get('_id') == str(user.get('_id')):
|
||||
if str(post.get('_id')) == doc.get('post_id') and post.get('status') not in ['Продано', 'Приглашение отклонено'] and doc.get('status') not in ['Приглашение отклонено'] and doc.get('receiver') and doc.get('receiver').get('_id') == str(user.get('_id')):
|
||||
post.update({'status':doc.get('status'), 'receiver': doc.get('receiver'), 'd_id':doc.get('_id')})
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="profile/partner/exhibition.html", context={"now": datetime.now(), "user": user, "exhibition": exhibition, "posts": posts}
|
||||
|
||||
@ -1,85 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "90b31bac",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# ignore\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"def load_env(file_path):\n",
|
||||
" \"\"\"Загружает переменные окружения из файла .env с поддержкой булевых значений.\"\"\"\n",
|
||||
" with open(file_path) as f:\n",
|
||||
" for line in f:\n",
|
||||
" line = line.strip()\n",
|
||||
" if not line or line.startswith('#'):\n",
|
||||
" continue\n",
|
||||
" \n",
|
||||
" try:\n",
|
||||
" key, value = line.split('=', 1)\n",
|
||||
" key = key.strip()\n",
|
||||
" value = value.strip('\"\\' ')\n",
|
||||
" # Преобразование строковых значений в булевые\n",
|
||||
" if value.lower() in ['true', '1', 'yes']:\n",
|
||||
" value = True\n",
|
||||
" elif value.lower() in ['false', '0', 'no']:\n",
|
||||
" value = False\n",
|
||||
" \n",
|
||||
" # Проверка, что ключ не пустой\n",
|
||||
" if key:\n",
|
||||
" os.environ[key] = str(value) # Храним как строку\n",
|
||||
" except ValueError:\n",
|
||||
" print(f\"Ошибка: неверный формат в строке '{line}'\")\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" load_env('env')\n",
|
||||
"except:\n",
|
||||
" load_env('env_example')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c3f5835c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from db import *"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d80807dd",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"await posts_collection.update_many({'status':'publish'},{'$set':{'status':'В каталоге'}})"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.13.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
from db import *
|
||||
|
||||
await posts_collection.update_many({'status':'publish'},{'$set':{'status':'В каталоге'}})
|
||||
@ -2,7 +2,7 @@ importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.0.0/workbox
|
||||
|
||||
workbox.setConfig({ debug: false });
|
||||
|
||||
const CACHE_NAME = 'app-cache-v2.1.4';
|
||||
const CACHE_NAME = 'app-cache-v2.1.5';
|
||||
const MIN_VALID_SIZE = 500;
|
||||
|
||||
const ASSETS_TO_CACHE = [
|
||||
|
||||
@ -2,7 +2,7 @@ importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.0.0/workbox
|
||||
|
||||
workbox.setConfig({ debug: false });
|
||||
|
||||
const CACHE_NAME = 'app-cache-v2.1.4';
|
||||
const CACHE_NAME = 'app-cache-v2.1.5';
|
||||
const MIN_VALID_SIZE = 500;
|
||||
|
||||
const ASSETS_TO_CACHE = [
|
||||
|
||||
15
tasks.ipynb
15
tasks.ipynb
@ -676,14 +676,17 @@
|
||||
" if other_ids:\n",
|
||||
" await delivery_collection.update_many(\n",
|
||||
" {'post_id': {'$in': other_ids}},\n",
|
||||
" {'$set': {'status': 'Приглашение откланено'}} # как вы написали\n",
|
||||
" {'$set': {'status': 'Приглашение отклонено'}} # как вы написали\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" if (exhibition:=invitation.get('exhibition',{})) and (exhibition_id:=invitation.get('exhibition_id')):\n",
|
||||
" link = f'/profile/delivery/exhibition/{exhibition_id}'\n",
|
||||
" else:\n",
|
||||
" link = f'/profile/delivery'\n",
|
||||
" await notify_in_profile(NotifyItem(\n",
|
||||
" user_id=invitation.get('exhibition',{}).get(\"user\",{}).get('_id'),\n",
|
||||
" user_id=exhibition.get(\"user\",{}).get('_id'),\n",
|
||||
" type_item='c',\n",
|
||||
" title=f'Выставка \"{invitation.get('exhibition',{}).get(\"title\")}\"',\n",
|
||||
" link=f'/profile/delivery',\n",
|
||||
" title=f'Выставка \"{exhibition.get(\"title\")}\"',\n",
|
||||
" link=link,\n",
|
||||
" text=f'Художник принял условия'\n",
|
||||
" ))"
|
||||
],
|
||||
@ -700,7 +703,7 @@
|
||||
" exhibition = await exhibition_collection.find_one({'_id': ObjectId(exhibition_id)})\n",
|
||||
" exhibition_owner = exhibition.get('user')\n",
|
||||
" obj_ids = [ObjectId(x) for x in exhibition.get('posts', [])]\n",
|
||||
" posts = await posts_collection.find({\"_id\": {\"$in\": obj_ids}}).to_list() if obj_ids else []\n",
|
||||
" posts = await posts_collection.find({\"_id\": {\"$in\": obj_ids}, 'status': 'В каталоге'}).to_list() if obj_ids else []\n",
|
||||
" new_posts = {}\n",
|
||||
" all_posts = {}\n",
|
||||
" for post in posts:\n",
|
||||
|
||||
15
tasks.py
15
tasks.py
@ -514,14 +514,17 @@ async def accept_invitations(invitations_id: str, posts_ids: List[str]):
|
||||
if other_ids:
|
||||
await delivery_collection.update_many(
|
||||
{'post_id': {'$in': other_ids}},
|
||||
{'$set': {'status': 'Приглашение откланено'}} # как вы написали
|
||||
{'$set': {'status': 'Приглашение отклонено'}} # как вы написали
|
||||
)
|
||||
|
||||
if (exhibition:=invitation.get('exhibition',{})) and (exhibition_id:=invitation.get('exhibition_id')):
|
||||
link = f'/profile/delivery/exhibition/{exhibition_id}'
|
||||
else:
|
||||
link = f'/profile/delivery'
|
||||
await notify_in_profile(NotifyItem(
|
||||
user_id=invitation.get('exhibition',{}).get("user",{}).get('_id'),
|
||||
user_id=exhibition.get("user",{}).get('_id'),
|
||||
type_item='c',
|
||||
title=f'Выставка "{invitation.get('exhibition',{}).get("title")}"',
|
||||
link=f'/profile/delivery',
|
||||
title=f'Выставка "{exhibition.get("title")}"',
|
||||
link=link,
|
||||
text=f'Художник принял условия'
|
||||
))
|
||||
|
||||
@ -529,7 +532,7 @@ async def exhibition_send_invitations(exhibition_id):
|
||||
exhibition = await exhibition_collection.find_one({'_id': ObjectId(exhibition_id)})
|
||||
exhibition_owner = exhibition.get('user')
|
||||
obj_ids = [ObjectId(x) for x in exhibition.get('posts', [])]
|
||||
posts = await posts_collection.find({"_id": {"$in": obj_ids}}).to_list() if obj_ids else []
|
||||
posts = await posts_collection.find({"_id": {"$in": obj_ids}, 'status': 'В каталоге'}).to_list() if obj_ids else []
|
||||
new_posts = {}
|
||||
all_posts = {}
|
||||
for post in posts:
|
||||
|
||||
@ -37,7 +37,6 @@
|
||||
max-width: 100%;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-word;
|
||||
hyphens: auto;
|
||||
}
|
||||
.c_qx7jk{margin: 100px 0;}
|
||||
.c_h9bsw{width: 100%;object-fit: scale-down;}
|
||||
|
||||
@ -18,7 +18,6 @@ h1,h2,h3,p {
|
||||
max-width: 100%;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-word;
|
||||
hyphens: auto;
|
||||
}
|
||||
html{display: flex;justify-content: center;}
|
||||
input, button {touch-action: manipulation;}
|
||||
|
||||
@ -149,7 +149,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// 2) newest first
|
||||
const aTime = parseDate(a.dateStr)?.getTime() ?? 0;
|
||||
const bTime = parseDate(b.dateStr)?.getTime() ?? 0;
|
||||
return bTime - aTime;
|
||||
return aTime - bTime;
|
||||
});
|
||||
|
||||
notificationsArray.forEach(item => {
|
||||
|
||||
@ -11,11 +11,8 @@
|
||||
<section class="c_72077">
|
||||
<div class="c_q7xgt b_vnic7">
|
||||
<div class="c_w3iz6">
|
||||
<div class="c_k9ibf b_6fwyq" style="grid-area: b;">
|
||||
<div class="c_k9ibf b_6fwyq" style="grid-area: b;padding: 0;">
|
||||
<div class="b_ornr9">
|
||||
<div
|
||||
style="position:absolute;inset:0;background:url('/b/exhibition/{{ exhibition.poster_file_id }}') no-repeat center/cover;filter:blur(3px)">
|
||||
</div>
|
||||
<picture>
|
||||
<img crossorigin="anonymous" fetchpriority="high" loading="eager" alt="{{ exhibition.title }}" class="b_ornr9"
|
||||
src="/b/exhibition/{{ exhibition.poster_file_id }}" alt="Выставка {{exhibition.title }}">
|
||||
@ -30,14 +27,16 @@
|
||||
</svg>
|
||||
<span class="b_ww2ac">Назад</span>
|
||||
</a>
|
||||
{% if exhibition.get('status',-1) == 2 %}
|
||||
<div class="b_thlt1_2">
|
||||
<span class="b_5dr47_2">Выставка закончилась</span>
|
||||
</div>
|
||||
<div class="{% if now > exhibition.date_finish %}b_vabj9_2{%else%}b_vabj9{% endif %}">
|
||||
{% if now > exhibition.date_finish %}
|
||||
<span class="c_95csi b_0jst1_2">Закончилась</span>
|
||||
{% elif now > exhibition.datetime_start %}
|
||||
<span class="c_95csi b_0jst1">Началась</span>
|
||||
{% else %}
|
||||
<div class="b_thlt1">
|
||||
<span class="b_5dr47">Идет выставка</span>
|
||||
<span class="c_95csi b_0jst1">Формируется</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if now < exhibition.date_finish %}
|
||||
<span class="b_sdf">До {{ (exhibition.date_finish.strftime('%d.%m.%Y')) if exhibition.date_finish else ''}}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
@ -95,6 +94,15 @@
|
||||
<header class="c_zjvne b_tt84a">
|
||||
<h2 class="c_6kn8d b_uilob">{{post.title}}</h2>
|
||||
</header>
|
||||
{% if user and (user._id == post.user._id or 'partner' in user.get('roles',[]))%}
|
||||
<label class="offer-label delivery-checkbox-label">
|
||||
<input type="checkbox"
|
||||
onchange="saledPaintings('{{post._id}}')"
|
||||
class="offer-checkbox delivery-checkbox send-checkbox"
|
||||
{% if post.status == 'Продано' %}checked{%endif%}>
|
||||
<span>Продано</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
<div class="b_xz6em" ><img class="b_ob59y" src="/a/{{post.user._id}}" crossorigin="anonymous" fetchpriority="high" loading="eager">
|
||||
<div class="c_9hjdk b_ffruz">
|
||||
<p class="b_qla2q">{{post.city or ''}}</p>
|
||||
@ -109,7 +117,38 @@
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function saledPaintings(postId) {
|
||||
fetch(`/api/post/${postId}/sale`, {
|
||||
method: 'PUT'
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
// Find and delete the parent figure
|
||||
const postElement = document.querySelector(`figure[data-postId='${postId}']`);
|
||||
showToast({
|
||||
message: 'Сохранено!',
|
||||
status: 'info',
|
||||
duration: 1200
|
||||
});
|
||||
} else {
|
||||
showToast({
|
||||
message: 'Ошибка соединения с сервером!',
|
||||
status: 'error',
|
||||
duration: 2500
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// Handle network errors
|
||||
showToast({
|
||||
message: 'Ошибка соединения с сервером!',
|
||||
status: 'error',
|
||||
duration: 2500
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
(function(){
|
||||
const input = document.getElementById('memberSearch');
|
||||
@ -157,6 +196,11 @@
|
||||
<meta property="og:site_name" content="Конкурсы | KULTMOST.АВТОПОСТ">
|
||||
{{ header.style() }}
|
||||
<style>
|
||||
.b_0jst1 { color: #0ED646; font-family: Inter; font-size: 12px; font-style: normal; font-weight: 700; line-height: normal; }
|
||||
.b_vabj9 { align-items: center; background: rgba(14, 214, 70, 0.20); border-radius: 30px; display: flex; justify-content: center; padding: 1.5px 10px; }
|
||||
.b_0jst1_2 { color: #5F1798; font-family: Inter; font-size: 12px; font-style: normal; font-weight: 700; line-height: normal; }
|
||||
.b_vabj9_2 { align-items: center; background: #5e17982f; border-radius: 30px; display: flex; justify-content: center; padding: 1.5px 10px; }
|
||||
.b_ornr9{ border-radius: 10px; cursor: zoom-in; }
|
||||
.b_thlt1 { align-items: center; background: rgba(14, 214, 70, 0.20); border-radius: 30px; display: flex; justify-content: center; padding: 1.5px 10px; }
|
||||
.b_5dr47 { color: #0ED646; font-family: Inter; font-size: 12px; font-style: normal; font-weight: 700; line-height: normal; }
|
||||
.b_thlt1_2 { align-items: center; background: rgba(95, 23, 152, 0.20); border-radius: 30px; display: flex; justify-content: center; padding: 1.5px 10px; }
|
||||
|
||||
@ -22,9 +22,6 @@
|
||||
<a href="{{link}}">
|
||||
{% endif %}
|
||||
<div class="b_ornr9">
|
||||
<div
|
||||
style="position:absolute;inset:0;background:url('/b/exhibition/{{ exhibition.poster_file_id }}') no-repeat center/cover;filter:blur(3px)">
|
||||
</div>
|
||||
<picture>
|
||||
<img crossorigin="anonymous" fetchpriority="high" loading="eager" alt="{{ exhibition.title }}" class="b_ornr9"
|
||||
src="/b/exhibition/{{ exhibition.poster_file_id }}" alt="Выставка {{exhibition.title }}">
|
||||
@ -37,16 +34,15 @@
|
||||
{% if link %}
|
||||
<a href="{{link}}" class="b_73p10">
|
||||
{% endif %}
|
||||
{% if exhibition.get('status',-1) == 2 %}
|
||||
<div class="b_vabj9_2">
|
||||
<span class="b_0jst1_2">Выставка закончилась</span>
|
||||
</div>
|
||||
<div class="{% if now > exhibition.date_finish %}b_vabj9_2{%else%}b_vabj9{% endif %}">
|
||||
{% if now > exhibition.date_finish %}
|
||||
<span class="c_95csi b_0jst1_2">Закончилась</span>
|
||||
{% elif now > exhibition.datetime_start %}
|
||||
<span class="c_95csi b_0jst1">Началась</span>
|
||||
{% else %}
|
||||
<div class="b_vabj9">
|
||||
<span class="b_0jst1">Идет выставка</span>
|
||||
</div>
|
||||
<!-- <span class="b_sdf">До {{ (exhibition.date_finish.strftime('%d.%m.%Y')) if exhibition.date_finish else ''}}</span> -->
|
||||
<span class="c_95csi b_0jst1">Формируется</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<header class="c_m876y b_9xtz9">
|
||||
<h3 class="c_hysjg b_8f3uh">{{ exhibition.title }}</h3>
|
||||
</header>
|
||||
@ -110,7 +106,7 @@ picture>img{ width: 100%; object-fit: scale-down; }
|
||||
.fl{display: flex;justify-content: space-between;width: 100%;}
|
||||
.c_som9s { color: #FFF; font-family: Inter; font-size: 14px; font-style: normal; font-weight: 400; line-height: normal; }
|
||||
.c_aq9et { display: flex; padding: 19px 33px; justify-content: center; align-items: center; align-self: stretch; border-radius: 5px; background: #5F1798; }
|
||||
.b_ornr9 { align-self: stretch; height: 400px; overflow: hidden;}
|
||||
.b_ornr9 { align-self: stretch; overflow: hidden;}
|
||||
.b_0jst1 { color: #0ED646; font-family: Inter; font-size: 12px; font-style: normal; font-weight: 700; line-height: normal; }
|
||||
.b_vabj9 { align-items: center; background: rgba(14, 214, 70, 0.20); border-radius: 30px; display: flex; justify-content: center; padding: 1.5px 10px; }
|
||||
.b_0jst1_2 { color: #5F1798; font-family: Inter; font-size: 12px; font-style: normal; font-weight: 700; line-height: normal; }
|
||||
|
||||
@ -145,7 +145,11 @@
|
||||
.c_la1ap { color: rgba(58, 58, 58, 0.75); font-family: Inter; font-size: 14px; font-style: normal; font-weight: 400; line-height: normal; }
|
||||
.c_w6ecf { -webkit-box-orient: vertical; -webkit-line-clamp: 1; align-self: stretch; color: #000; display: -webkit-box; font-family: Inter; font-size: 20px; font-style: normal; font-weight: 700; line-height: normal; overflow: hidden; text-overflow: ellipsis; }
|
||||
.c_m3jeo { align-content: flex-start; align-items: flex-start; background: #FFF; display: flex; flex-wrap: wrap; gap: 30px; justify-content: center; width: 1600px; }
|
||||
}</style>
|
||||
}
|
||||
.b_gagrr { align-items: center; align-self: stretch; display: flex; justify-content: space-between; }
|
||||
.b_rhbo8 { color: #5F1798; font-family: Inter; font-size: 14px; font-style: normal; font-weight: 400; line-height: normal; }
|
||||
.c_eiyz0{cursor: pointer;}
|
||||
</style>
|
||||
{{ footer.style() }}
|
||||
{% endblock %}
|
||||
|
||||
@ -182,13 +186,58 @@
|
||||
</div>
|
||||
<p class="c_zlnpy b_7efyd">{{paint.description or ''}}</p>
|
||||
<p class="c_sbnel b_frv5u">{{ (paint._id.generation_time.strftime('%d.%m.%Y')) if paint._id else ''}}</p>
|
||||
<label class="offer-label delivery-checkbox-label">
|
||||
<input type="checkbox"
|
||||
onchange="saledPaintings('{{paint._id}}')"
|
||||
class="offer-checkbox delivery-checkbox send-checkbox"
|
||||
{% if paint.status == 'Продано' %}checked{%endif%}>
|
||||
<span>Продано</span>
|
||||
</label>
|
||||
{% if user and user._id == paint.user._id %}
|
||||
<div class="c_ybfp2 b_gagrr" style="max-width: 320px;">
|
||||
<a class="c_eiyz0 b_rhbo8" href="/form/post/{{paint._id}}">Редактировать</a>
|
||||
<button class="c_eiyz0 b_rhbo8" onclick="deletePost('{{paint._id}}')">Удалить</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<a class="c_75ykb b_gk0ob" href="https://telegram.me/gennadiy0605"><span class="c_dlut5 b_v0d1u">Обсудить покупку картины </span></a>
|
||||
{% endif %}
|
||||
<div class="c_sbnel b_frv5u">{{ share.share('/paintings/{}'.format(paint._id), paint.title, '/b/posts/{}'.format(paint.medias[0])) }}</div>
|
||||
<p class="c_sbnel b_frv5u">👁 {{total_watch_count}} (+{{today_watch_count}} Сегодня)</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
<script>
|
||||
function saledPaintings(postId) {
|
||||
fetch(`/api/post/${postId}/sale`, {
|
||||
method: 'PUT'
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
// Find and delete the parent figure
|
||||
const postElement = document.querySelector(`figure[data-postId='${postId}']`);
|
||||
showToast({
|
||||
message: 'Сохранено!',
|
||||
status: 'info',
|
||||
duration: 1200
|
||||
});
|
||||
} else {
|
||||
showToast({
|
||||
message: 'Ошибка соединения с сервером!',
|
||||
status: 'error',
|
||||
duration: 2500
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// Handle network errors
|
||||
showToast({
|
||||
message: 'Ошибка соединения с сервером!',
|
||||
status: 'error',
|
||||
duration: 2500
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
const images = [
|
||||
{% for media in paint.medias_original %}
|
||||
@ -296,9 +345,35 @@
|
||||
init();
|
||||
setTimeout(updateTransform, 100); // даём браузеру время на рендер
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
function deletePost(postId) {
|
||||
fetch(`/api/post/${postId}`, {
|
||||
method: 'DELETE'
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
// Find and delete the parent figure
|
||||
const postElement = document.querySelector(`figure[data-postId='${postId}']`);
|
||||
if (postElement) {
|
||||
postElement.remove();
|
||||
}
|
||||
} else {
|
||||
showToast({
|
||||
message: 'Ошибка соединения с сервером!',
|
||||
status: 'error',
|
||||
duration: 2500
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// Handle network errors
|
||||
showToast({
|
||||
message: 'Ошибка соединения с сервером!',
|
||||
status: 'error',
|
||||
duration: 2500
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{{ footer.section(user) }}
|
||||
{% endblock %}
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
<span class="b_5dr47_2">{{doc.status}}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if doc.type == 'exhibition' and doc.get('exhibition') %}
|
||||
{% if doc.type == 'exhibition' and doc.get('exhibition') or doc.status == 'Продано' %}
|
||||
<p class="b_6spaf">{{doc.exhibition.title}}</p>
|
||||
<p>Место проведения: {{ doc.exhibition.get('place') }}</p>
|
||||
<div class="b_xz6em">
|
||||
@ -54,6 +54,57 @@
|
||||
<span class="c_zm0m4 b_09est">{{doc.receiver.username}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if doc.get('user') and user._id == doc.user._id%}
|
||||
<details style="margin-bottom: 1.25rem;">
|
||||
<summary>Показать контакты</summary>
|
||||
<div class="menua">
|
||||
<a class="icona" href="/artists/{{doc.receiver._id}}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M234-276q51-39 114-61.5T480-360q69 0 132 22.5T726-276q35-41 54.5-93T800-480q0-133-93.5-226.5T480-800q-133 0-226.5 93.5T160-480q0 59 19.5 111t54.5 93Zm146.5-204.5Q340-521 340-580t40.5-99.5Q421-720 480-720t99.5 40.5Q620-639 620-580t-40.5 99.5Q539-440 480-440t-99.5-40.5ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm100-95.5q47-15.5 86-44.5-39-29-86-44.5T480-280q-53 0-100 15.5T294-220q39 29 86 44.5T480-160q53 0 100-15.5ZM523-537q17-17 17-43t-17-43q-17-17-43-17t-43 17q-17 17-17 43t17 43q17 17 43 17t43-17Zm-43-43Zm0 360Z" />
|
||||
</svg>
|
||||
<span>Профиль</span>
|
||||
</a>
|
||||
<a class="icona" href="tel:{{doc.receiver.phone.split(',')[-1].strip()}}"><svg xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M162-120q-18 0-30-12t-12-30v-162q0-13 9-23.5t23-14.5l138-28q14-2 28.5 2.5T342-374l94 94q38-22 72-48.5t65-57.5q33-32 60.5-66.5T681-524l-97-98q-8-8-11-19t-1-27l26-140q2-13 13-22.5t25-9.5h162q18 0 30 12t12 30q0 125-54.5 247T631-329Q531-229 409-174.5T162-120Zm556-480q17-39 26-79t14-81h-88l-18 94 66 66ZM360-244l-66-66-94 20v88q41-3 81-14t79-28Zm358-356ZM360-244Z" />
|
||||
</svg>
|
||||
<span>{{ doc.receiver.phone.split(',')[-1].strip() }}</span>
|
||||
</a>
|
||||
<a class="icona" href="mailto:{{doc.receiver.email}}"> <svg xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480v58q0 59-40.5 100.5T740-280q-35 0-66-15t-52-43q-29 29-65.5 43.5T480-280q-83 0-141.5-58.5T280-480q0-83 58.5-141.5T480-680q83 0 141.5 58.5T680-480v58q0 26 17 44t43 18q26 0 43-18t17-44v-58q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93h200v80H480Zm85-315q35-35 35-85t-35-85q-35-35-85-35t-85 35q-35 35-35 85t35 85q35 35 85 35t85-35Z" />
|
||||
</svg>
|
||||
<span>{{doc.receiver.email}}</span>
|
||||
</a>
|
||||
<div class="icona"><span>Написать</span></div>
|
||||
<div class="icona">
|
||||
|
||||
<a class="icona" href="{% if doc.receiver.vk_id %}https://vk.ru/im?sel={{doc.receiver.vk_id}}{%else%}#{%endif%}"
|
||||
{% if not doc.receiver.vk_id %}onclick="showToast({message: 'У пользователя не привязан ВК'})"{%endif%}>
|
||||
<img src="/static/icons/vk.svg" height="40">
|
||||
</a>
|
||||
|
||||
<a class="icona" href="https://telegram.me/{{doc.receiver.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/tg.svg" height="40">
|
||||
<!-- <span>Написать в ТГ</span> -->
|
||||
</a>
|
||||
<a class="icona" href="https://max.ru/{{doc.receiver.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/max.svg" height="40" style="border-radius: 50% 50% 50% 30%;">
|
||||
<!-- <span>Написать в MAX</span> -->
|
||||
</a>
|
||||
<a class="icona" href="https://wa.me/{{doc.receiver.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/wapp.svg" height="40">
|
||||
<!-- <span>Написать в WA</span> -->
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</details>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if doc.address %}
|
||||
<p class="b_6spaf">Адрес: {{doc.address}}</p>
|
||||
@ -75,6 +126,57 @@
|
||||
<span class="c_zm0m4 b_09est">{{doc.sender.username}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if user._id != doc.sender._id%}
|
||||
<details style="margin-bottom: 1.25rem;">
|
||||
<summary>Показать контакты</summary>
|
||||
<div class="menua">
|
||||
<a class="icona" href="/artists/{{doc.sender._id}}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M234-276q51-39 114-61.5T480-360q69 0 132 22.5T726-276q35-41 54.5-93T800-480q0-133-93.5-226.5T480-800q-133 0-226.5 93.5T160-480q0 59 19.5 111t54.5 93Zm146.5-204.5Q340-521 340-580t40.5-99.5Q421-720 480-720t99.5 40.5Q620-639 620-580t-40.5 99.5Q539-440 480-440t-99.5-40.5ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm100-95.5q47-15.5 86-44.5-39-29-86-44.5T480-280q-53 0-100 15.5T294-220q39 29 86 44.5T480-160q53 0 100-15.5ZM523-537q17-17 17-43t-17-43q-17-17-43-17t-43 17q-17 17-17 43t17 43q17 17 43 17t43-17Zm-43-43Zm0 360Z" />
|
||||
</svg>
|
||||
<span>Профиль</span>
|
||||
</a>
|
||||
<a class="icona" href="tel:{{doc.sender.phone.split(',')[-1].strip()}}"><svg xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M162-120q-18 0-30-12t-12-30v-162q0-13 9-23.5t23-14.5l138-28q14-2 28.5 2.5T342-374l94 94q38-22 72-48.5t65-57.5q33-32 60.5-66.5T681-524l-97-98q-8-8-11-19t-1-27l26-140q2-13 13-22.5t25-9.5h162q18 0 30 12t12 30q0 125-54.5 247T631-329Q531-229 409-174.5T162-120Zm556-480q17-39 26-79t14-81h-88l-18 94 66 66ZM360-244l-66-66-94 20v88q41-3 81-14t79-28Zm358-356ZM360-244Z" />
|
||||
</svg>
|
||||
<span>{{ doc.sender.phone.split(',')[-1].strip() }}</span>
|
||||
</a>
|
||||
<a class="icona" href="mailto:{{doc.sender.email}}"> <svg xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480v58q0 59-40.5 100.5T740-280q-35 0-66-15t-52-43q-29 29-65.5 43.5T480-280q-83 0-141.5-58.5T280-480q0-83 58.5-141.5T480-680q83 0 141.5 58.5T680-480v58q0 26 17 44t43 18q26 0 43-18t17-44v-58q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93h200v80H480Zm85-315q35-35 35-85t-35-85q-35-35-85-35t-85 35q-35 35-35 85t35 85q35 35 85 35t85-35Z" />
|
||||
</svg>
|
||||
<span>{{doc.sender.email}}</span>
|
||||
</a>
|
||||
<div class="icona"><span>Написать</span></div>
|
||||
<div class="icona">
|
||||
|
||||
<a class="icona" href="{% if doc.sender.vk_id %}https://vk.ru/im?sel={{doc.sender.vk_id}}{%else%}#{%endif%}"
|
||||
{% if not doc.sender.vk_id %}onclick="showToast({message: 'У пользователя не привязан ВК'})"{%endif%}>
|
||||
<img src="/static/icons/vk.svg" height="40">
|
||||
</a>
|
||||
|
||||
<a class="icona" href="https://telegram.me/{{doc.sender.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/tg.svg" height="40">
|
||||
<!-- <span>Написать в ТГ</span> -->
|
||||
</a>
|
||||
<a class="icona" href="https://max.ru/{{doc.sender.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/max.svg" height="40" style="border-radius: 50% 50% 50% 30%;">
|
||||
<!-- <span>Написать в MAX</span> -->
|
||||
</a>
|
||||
<a class="icona" href="https://wa.me/{{doc.sender.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/wapp.svg" height="40">
|
||||
<!-- <span>Написать в WA</span> -->
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</details>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if doc.receiver %}
|
||||
<div class="b_xz6em">
|
||||
@ -85,6 +187,57 @@
|
||||
<span class="b_qla2q">+7 *** *** {{ doc.receiver.phone[-4:] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if user._id != doc.receiver._id%}
|
||||
<details style="margin-bottom: 1.25rem;">
|
||||
<summary>Показать контакты</summary>
|
||||
<div class="menua">
|
||||
<a class="icona" href="/artists/{{doc.receiver._id}}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M234-276q51-39 114-61.5T480-360q69 0 132 22.5T726-276q35-41 54.5-93T800-480q0-133-93.5-226.5T480-800q-133 0-226.5 93.5T160-480q0 59 19.5 111t54.5 93Zm146.5-204.5Q340-521 340-580t40.5-99.5Q421-720 480-720t99.5 40.5Q620-639 620-580t-40.5 99.5Q539-440 480-440t-99.5-40.5ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm100-95.5q47-15.5 86-44.5-39-29-86-44.5T480-280q-53 0-100 15.5T294-220q39 29 86 44.5T480-160q53 0 100-15.5ZM523-537q17-17 17-43t-17-43q-17-17-43-17t-43 17q-17 17-17 43t17 43q17 17 43 17t43-17Zm-43-43Zm0 360Z" />
|
||||
</svg>
|
||||
<span>Профиль</span>
|
||||
</a>
|
||||
<a class="icona" href="tel:{{doc.receiver.phone.split(',')[-1].strip()}}"><svg xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M162-120q-18 0-30-12t-12-30v-162q0-13 9-23.5t23-14.5l138-28q14-2 28.5 2.5T342-374l94 94q38-22 72-48.5t65-57.5q33-32 60.5-66.5T681-524l-97-98q-8-8-11-19t-1-27l26-140q2-13 13-22.5t25-9.5h162q18 0 30 12t12 30q0 125-54.5 247T631-329Q531-229 409-174.5T162-120Zm556-480q17-39 26-79t14-81h-88l-18 94 66 66ZM360-244l-66-66-94 20v88q41-3 81-14t79-28Zm358-356ZM360-244Z" />
|
||||
</svg>
|
||||
<span>{{ doc.receiver.phone.split(',')[-1].strip() }}</span>
|
||||
</a>
|
||||
<a class="icona" href="mailto:{{doc.receiver.email}}"> <svg xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480v58q0 59-40.5 100.5T740-280q-35 0-66-15t-52-43q-29 29-65.5 43.5T480-280q-83 0-141.5-58.5T280-480q0-83 58.5-141.5T480-680q83 0 141.5 58.5T680-480v58q0 26 17 44t43 18q26 0 43-18t17-44v-58q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93h200v80H480Zm85-315q35-35 35-85t-35-85q-35-35-85-35t-85 35q-35 35-35 85t35 85q35 35 85 35t85-35Z" />
|
||||
</svg>
|
||||
<span>{{doc.receiver.email}}</span>
|
||||
</a>
|
||||
<div class="icona"><span>Написать</span></div>
|
||||
<div class="icona">
|
||||
|
||||
<a class="icona" href="{% if doc.receiver.vk_id %}https://vk.ru/im?sel={{doc.receiver.vk_id}}{%else%}#{%endif%}"
|
||||
{% if not doc.receiver.vk_id %}onclick="showToast({message: 'У пользователя не привязан ВК'})"{%endif%}>
|
||||
<img src="/static/icons/vk.svg" height="40">
|
||||
</a>
|
||||
|
||||
<a class="icona" href="https://telegram.me/{{doc.receiver.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/tg.svg" height="40">
|
||||
<!-- <span>Написать в ТГ</span> -->
|
||||
</a>
|
||||
<a class="icona" href="https://max.ru/{{doc.receiver.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/max.svg" height="40" style="border-radius: 50% 50% 50% 30%;">
|
||||
<!-- <span>Написать в MAX</span> -->
|
||||
</a>
|
||||
<a class="icona" href="https://wa.me/{{doc.receiver.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/wapp.svg" height="40">
|
||||
<!-- <span>Написать в WA</span> -->
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</details>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<p class="b_6spaf">
|
||||
|
||||
@ -31,8 +31,16 @@
|
||||
|
||||
<a class="b_6spaf" href="/profile/delivery/{{doc.post_id}}#main">Посмотреть историю</a>
|
||||
<h2 class="c_9hvtd b_f5fb2">{{doc.title}}</h2>
|
||||
|
||||
{% if doc.type == 'exhibition' and doc.get('exhibition') %}
|
||||
{% if doc.status == 'На выставке' %}
|
||||
<label class="offer-label">
|
||||
<input type="checkbox"
|
||||
onchange="saledPaintings('{{doc.post_id}}')"
|
||||
class="offer-checkbox"
|
||||
{% if doc.status == 'Продано' %}checked{%endif%}>
|
||||
<span>Продано</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
{% if doc.type == 'exhibition' and doc.get('exhibition') or doc.status == 'Продано' %}
|
||||
<p class="b_6spaf">{{doc.get('exhibition').title}}</p>
|
||||
<div class="b_xz6em">
|
||||
<img src="/a/{{doc.receiver._id}}" class="b_ob59y">
|
||||
@ -41,6 +49,57 @@
|
||||
<span class="c_zm0m4 b_09est">{{doc.receiver.username}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if doc.get('user') and user._id == doc.user._id%}
|
||||
<details style="margin-bottom: 1.25rem;">
|
||||
<summary>Показать контакты</summary>
|
||||
<div class="menua">
|
||||
<a class="icona" href="/artists/{{doc.receiver._id}}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M234-276q51-39 114-61.5T480-360q69 0 132 22.5T726-276q35-41 54.5-93T800-480q0-133-93.5-226.5T480-800q-133 0-226.5 93.5T160-480q0 59 19.5 111t54.5 93Zm146.5-204.5Q340-521 340-580t40.5-99.5Q421-720 480-720t99.5 40.5Q620-639 620-580t-40.5 99.5Q539-440 480-440t-99.5-40.5ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm100-95.5q47-15.5 86-44.5-39-29-86-44.5T480-280q-53 0-100 15.5T294-220q39 29 86 44.5T480-160q53 0 100-15.5ZM523-537q17-17 17-43t-17-43q-17-17-43-17t-43 17q-17 17-17 43t17 43q17 17 43 17t43-17Zm-43-43Zm0 360Z" />
|
||||
</svg>
|
||||
<span>Профиль</span>
|
||||
</a>
|
||||
<a class="icona" href="tel:{{doc.receiver.phone.split(',')[-1].strip()}}"><svg xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M162-120q-18 0-30-12t-12-30v-162q0-13 9-23.5t23-14.5l138-28q14-2 28.5 2.5T342-374l94 94q38-22 72-48.5t65-57.5q33-32 60.5-66.5T681-524l-97-98q-8-8-11-19t-1-27l26-140q2-13 13-22.5t25-9.5h162q18 0 30 12t12 30q0 125-54.5 247T631-329Q531-229 409-174.5T162-120Zm556-480q17-39 26-79t14-81h-88l-18 94 66 66ZM360-244l-66-66-94 20v88q41-3 81-14t79-28Zm358-356ZM360-244Z" />
|
||||
</svg>
|
||||
<span>{{ doc.receiver.phone.split(',')[-1].strip() }}</span>
|
||||
</a>
|
||||
<a class="icona" href="mailto:{{doc.receiver.email}}"> <svg xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480v58q0 59-40.5 100.5T740-280q-35 0-66-15t-52-43q-29 29-65.5 43.5T480-280q-83 0-141.5-58.5T280-480q0-83 58.5-141.5T480-680q83 0 141.5 58.5T680-480v58q0 26 17 44t43 18q26 0 43-18t17-44v-58q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93h200v80H480Zm85-315q35-35 35-85t-35-85q-35-35-85-35t-85 35q-35 35-35 85t35 85q35 35 85 35t85-35Z" />
|
||||
</svg>
|
||||
<span>{{doc.receiver.email}}</span>
|
||||
</a>
|
||||
<div class="icona"><span>Написать</span></div>
|
||||
<div class="icona">
|
||||
|
||||
<a class="icona" href="{% if doc.receiver.vk_id %}https://vk.ru/im?sel={{doc.receiver.vk_id}}{%else%}#{%endif%}"
|
||||
{% if not doc.receiver.vk_id %}onclick="showToast({message: 'У пользователя не привязан ВК'})"{%endif%}>
|
||||
<img src="/static/icons/vk.svg" height="40">
|
||||
</a>
|
||||
|
||||
<a class="icona" href="https://telegram.me/{{doc.receiver.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/tg.svg" height="40">
|
||||
<!-- <span>Написать в ТГ</span> -->
|
||||
</a>
|
||||
<a class="icona" href="https://max.ru/{{doc.receiver.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/max.svg" height="40" style="border-radius: 50% 50% 50% 30%;">
|
||||
<!-- <span>Написать в MAX</span> -->
|
||||
</a>
|
||||
<a class="icona" href="https://wa.me/{{doc.receiver.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/wapp.svg" height="40">
|
||||
<!-- <span>Написать в WA</span> -->
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</details>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if doc.address %}
|
||||
<p class="b_6spaf">Адрес: {{doc.address}}</p>
|
||||
@ -216,15 +275,47 @@
|
||||
<div class="c_ru432">
|
||||
{{ profile_nav.profile_nav(user, 'delivery') }}
|
||||
<div class="c_tihtk" id="main">
|
||||
|
||||
{% set statuses = ['Ожидает отправки', 'Возврат художнику'] %}
|
||||
{% if data | selectattr('status', 'in', statuses) | list | length > 0 %}
|
||||
{% set statusAccept = ['Ожидает получения'] %}
|
||||
|
||||
{% set first =
|
||||
data
|
||||
| selectattr('status', 'in', statuses)
|
||||
| list
|
||||
| length > 0
|
||||
%}
|
||||
|
||||
{% set second =
|
||||
(
|
||||
data
|
||||
| selectattr('status', 'in', statusAccept)
|
||||
| list
|
||||
| length > 0
|
||||
)
|
||||
and
|
||||
(
|
||||
data
|
||||
| selectattr('receiver._id', 'equalto', user._id)
|
||||
| list
|
||||
| length > 0
|
||||
)
|
||||
%}
|
||||
|
||||
{% if first or second %}
|
||||
<article class="c_9fgdh b_ax8vz">
|
||||
<div class="c_z3dpx b_73p10">
|
||||
{% if first %}
|
||||
<header class="c_m876y b_9xtz9">
|
||||
<h3 class="b_8f3uh">Настройки отправки</h3>
|
||||
</header>
|
||||
<p class="b_tfnz2">Можно менять перед каждой отправкой картины</p>
|
||||
<label class="offer-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="offer-checkbox"
|
||||
onchange="selectAll(this, true)"
|
||||
>
|
||||
<span class="offer-text">Выбрать все картины</span>
|
||||
</label>
|
||||
<div class="b_73p10">
|
||||
<div class="c_aihm6 floating-group">
|
||||
<select class="c_xhd5k" id="delivery_type" name="delivery_type" required>
|
||||
@ -239,6 +330,20 @@
|
||||
<label for="track" class="c_z3ybu floating-label">Трэк-номер</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if second %}
|
||||
<header class="c_m876y b_9xtz9">
|
||||
<h3 class="b_8f3uh">Настройки получения</h3>
|
||||
</header>
|
||||
<label class="offer-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="offer-checkbox"
|
||||
onchange="selectAll(this, false)"
|
||||
>
|
||||
<span class="offer-text">Выбрать все картины</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
@ -250,12 +355,72 @@
|
||||
{# Sticky bottom action bar #}
|
||||
<div class="st" style="z-index: 2000;min-height: 101px;padding: 25px;gap: 5px;">
|
||||
<button class="b_wy3rt ba" id="btnSendSelected" style="display: none;" onclick="btnSendSelected()">
|
||||
<span class="b_bvl0d">Отправить выбранные</span>
|
||||
<span class="b_bvl0d">Отправить выбранные (<span id="sendCount">0</span>)</span>
|
||||
</button>
|
||||
<button class="b_wy3rt ba" id="btnAcceptSelected" style="display: none;" onclick="btnAcceptSelected()">
|
||||
<span class="b_bvl0d">Получить выбранные</span>
|
||||
<span class="b_bvl0d">Получить выбранные (<span id="acceptCount">0</span>)</span>
|
||||
</button>
|
||||
</div>
|
||||
<script>
|
||||
function saledPaintings(postId) {
|
||||
fetch(`/api/post/${postId}/sale`, {
|
||||
method: 'PUT'
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
// Find and delete the parent figure
|
||||
const postElement = document.querySelector(`figure[data-postId='${postId}']`);
|
||||
showToast({
|
||||
message: 'Сохранено!',
|
||||
status: 'info',
|
||||
duration: 1200
|
||||
});
|
||||
} else {
|
||||
showToast({
|
||||
message: 'Ошибка соединения с сервером!',
|
||||
status: 'error',
|
||||
duration: 2500
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// Handle network errors
|
||||
showToast({
|
||||
message: 'Ошибка соединения с сервером!',
|
||||
status: 'error',
|
||||
duration: 2500
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
function selectAll(elem, send=true){
|
||||
if (send){
|
||||
if (elem.checked){
|
||||
document.querySelectorAll('.send-checkbox').forEach(el=>{
|
||||
el.checked = true;
|
||||
})
|
||||
} else {
|
||||
document.querySelectorAll('.send-checkbox').forEach(el=>{
|
||||
el.checked = false;
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (elem.checked){
|
||||
document.querySelectorAll('.accept-checkbox').forEach(el=>{
|
||||
el.checked = true;
|
||||
})
|
||||
} else {
|
||||
document.querySelectorAll('.accept-checkbox').forEach(el=>{
|
||||
el.checked = false;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
updateStickyButtons();
|
||||
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
// ─────────────────────────────────────────────
|
||||
// Helpers: collect selected IDs
|
||||
@ -282,6 +447,12 @@
|
||||
const sendBtn = document.getElementById('btnSendSelected');
|
||||
const acceptBtn = document.getElementById('btnAcceptSelected');
|
||||
|
||||
const sendCountSpan = document.getElementById('sendCount');
|
||||
const acceptCountSpan = document.getElementById('acceptCount');
|
||||
|
||||
sendCountSpan.textContent = sendCount;
|
||||
acceptCountSpan.textContent = acceptCount;
|
||||
|
||||
if (sendBtn) sendBtn.style.display = sendCount > 0 ? 'inline-flex' : 'none';
|
||||
if (acceptBtn) acceptBtn.style.display = acceptCount > 0 ? 'inline-flex' : 'none';
|
||||
}
|
||||
@ -712,7 +883,7 @@ picture>img{ width: 100%; object-fit: scale-down; }
|
||||
.b_s6ool { white-space: pre-line; -webkit-box-orient: vertical; -webkit-line-clamp: 3; align-self: stretch; color: rgba(58, 58, 58, 0.75); display: -webkit-box; font-family: Inter; font-size: 14px; font-style: normal; font-weight: 400; line-height: normal; overflow: hidden; text-overflow: ellipsis; }
|
||||
.b_mp1wb { align-self: stretch; color: rgba(58, 58, 58, 0.75); font-family: Inter; font-size: 14px; font-style: normal; font-weight: 400; line-height: normal; text-align: right; }
|
||||
.b_73p10 { align-items: flex-start; align-self: stretch; background: #FFF; display: flex; flex-direction: column; gap: 10px; justify-content: center; }
|
||||
.c_z3dpx{ padding: 10px 20px;}
|
||||
.c_z3dpx{ padding: 10px 20px; gap: 20px; }
|
||||
.b_ax8vz { align-items: flex-start; border-radius: 10px; border: 1px solid #5F1798; box-shadow: 0 4px 4px 0 rgba(0, 0, 0, 0.30), 0 4px 20px 0 rgba(0, 0, 0, 0.20); display: flex; flex-direction: column; width: 335px; overflow: hidden;}
|
||||
@media (min-width: 901px) {
|
||||
.c_9fgdh { align-items: flex-start; border-radius: 10px; border: 1px solid #5F1798; box-shadow: 0 4px 4px 0 rgba(0, 0, 0, 0.30), 0 4px 20px 0 rgba(0, 0, 0, 0.20); display: flex; flex-direction: column; flex-shrink: 0; width: 335px; }
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<div class="c_tihtk" id="main">
|
||||
{% for i in data %}
|
||||
<article class="c_9fgdh b_ax8vz">
|
||||
<a href="/exhibition/{{i._id}}{% if i.status == 0 %}/catalog{%endif%}#main">
|
||||
<a href="/exhibition/{{i._id}}">
|
||||
<div class="b_ornr9">
|
||||
<picture>
|
||||
<img src="/b/exhibition/{{i.poster_file_id}}" data-src="/b/exhibition/{{i.poster_orig_id}}" id="preview_poster" crossorigin="anonymous" fetchpriority="high" loading="eager" alt="Загрузите афишу" class="b_ornr9">
|
||||
@ -26,7 +26,7 @@
|
||||
</div>
|
||||
</a>
|
||||
<div class="c_z3dpx b_73p10">
|
||||
<a class="b_73p10" href="/exhibition/{{i._id}}{% if i.status == 0 %}/catalog{%endif%}#main">
|
||||
<a class="b_73p10" href="/exhibition/{{i._id}}">
|
||||
<div class="c_c4wam b_vabj9">
|
||||
{% if now > i.date_finish %}
|
||||
<span class="c_95csi b_0jst1">Закончилась</span>
|
||||
|
||||
@ -158,6 +158,10 @@
|
||||
<header class="c_m876y b_9xtz9">
|
||||
<h3 class="c_hysjg b_8f3uh">{{ doc.title }}</h3>
|
||||
</header>
|
||||
{% if doc.get('type') == 'Выставка' %}
|
||||
<img src="/b/exhibition/{{doc.get('exhibition').get('poster_orig_id')}}"
|
||||
style="max-width: 100%;">
|
||||
{%endif%}
|
||||
<p class="c_2fr39 b_tfnz2">{{doc.get('exhibition').title }}</p>
|
||||
<p>Место проведения: {{ doc.get('exhibition').get('place') }}</p>
|
||||
<div class="b_otttb">
|
||||
@ -169,10 +173,7 @@
|
||||
''}}</span>
|
||||
</div>
|
||||
{{render_links(doc)}}
|
||||
<p>Картин выбрано: <span id="lenSelected">0</span></p>
|
||||
<button onclick="accept_inv('{{doc._id}}')" class="btn" style="width: 100%; box-sizing: border-box;">
|
||||
Принять условия
|
||||
</button>
|
||||
|
||||
{% set user_contact = doc.get('user') %}
|
||||
{{ render_user_contacts(user_contact) }}
|
||||
<p class="c_drpqj b_mp1wb">{{ (doc._id.generation_time.strftime('%d.%m.%Y')) if doc._id else ''}}</p>
|
||||
@ -181,6 +182,40 @@
|
||||
{% for post in doc.get('posts')%}
|
||||
{{render_post(post)}}
|
||||
{% endfor %}
|
||||
<div class="st" style="z-index: 2000;min-height: 101px;padding: 22px;gap: 5px;">
|
||||
<div style="
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
">Картин выбрано: <span id="lenSelected">0</span> <label class="offer-label delivery-checkbox-label">
|
||||
<input type="checkbox"
|
||||
class="offer-checkbox delivery-checkbox accept-checkbox"
|
||||
onchange="selecteAll(this)"
|
||||
data-id="{{doc._id}}">
|
||||
<span>Все</span>
|
||||
</label></div>
|
||||
<button onclick="accept_inv('{{doc._id}}')" class="btn" style="width: 100%; box-sizing: border-box;">
|
||||
Принять условия
|
||||
</button>
|
||||
</div>
|
||||
<script>
|
||||
function selecteAll(elem){
|
||||
if (elem.checked){
|
||||
document.querySelectorAll('.yes').forEach(el=>{
|
||||
el.checked = true;
|
||||
selectPost(el)
|
||||
})
|
||||
|
||||
} else {
|
||||
document.querySelectorAll('.yes').forEach(el=>{
|
||||
el.checked = false;
|
||||
uSelectPost(el)
|
||||
})
|
||||
}
|
||||
updateSelectedCount()
|
||||
|
||||
}
|
||||
</script>
|
||||
{%endif%}
|
||||
{% if doc.get('type_')=='paint' %}
|
||||
{% set statuses = ['Ожидает отправки', 'Возврат художнику'] %}
|
||||
@ -216,14 +251,7 @@
|
||||
{{ render_doc(doc) }}
|
||||
{% endfor %}
|
||||
{# Sticky bottom action bar #}
|
||||
<div class="st" style="z-index: 2000;min-height: 101px;padding: 25px;gap: 5px;">
|
||||
<button class="b_wy3rt ba" id="btnSendSelected" style="display: none;" onclick="btnSendSelected()">
|
||||
<span class="b_bvl0d">Отправить выбранные</span>
|
||||
</button>
|
||||
<button class="b_wy3rt ba" id="btnAcceptSelected" style="display: none;" onclick="btnAcceptSelected()">
|
||||
<span class="b_bvl0d">Получить выбранные</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleTrackField() {
|
||||
const deliveryType = document.getElementById('delivery_type');
|
||||
@ -714,6 +742,8 @@ document.getElementById('delivery_type')?.addEventListener('change', toggleTrack
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<details style="margin-bottom: 1.25rem;">
|
||||
<summary>Показать контакты</summary>
|
||||
<div class="menua">
|
||||
<a class="icona" href="tel:{{user.phone.split(',')[-1].strip()}}"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px" viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
@ -750,6 +780,7 @@ document.getElementById('delivery_type')?.addEventListener('change', toggleTrack
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
{% endmacro %}
|
||||
{% macro render_links(doc) %}
|
||||
{% if doc.get('exhibition')%}
|
||||
|
||||
@ -29,6 +29,8 @@
|
||||
<a class="btn ba" href="/profile/invitation/{{doc._id}}#main" style="width: 100%;">Открыть</a>
|
||||
{% endif %}
|
||||
{% if doc.get('type') == 'Выставка' %}
|
||||
<img src="/b/exhibition/{{doc.get('exhibition').get('poster_orig_id')}}"
|
||||
style="max-width: 100%;">
|
||||
<p class="c_2fr39 b_tfnz2">{{doc.get('exhibition').title }}</p>
|
||||
<p>Место проведения: {{ doc.get('exhibition').get('place') }}</p>
|
||||
<div class="b_otttb">
|
||||
@ -107,6 +109,8 @@ async function accept_inv(inv_id) {
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<details style="margin-bottom: 1.25rem;">
|
||||
<summary>Показать контакты</summary>
|
||||
<div class="menua">
|
||||
<a class="icona" href="tel:{{user.phone.split(',')[-1].strip()}}"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
height="24px" viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
@ -143,6 +147,7 @@ async function accept_inv(inv_id) {
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
{% endmacro %}
|
||||
{% block head %}
|
||||
{{ header.style() }}
|
||||
|
||||
@ -75,8 +75,17 @@
|
||||
<header class="c_m876y b_9xtz9">
|
||||
<h3 class="c_hysjg b_8f3uh" id="preview_title">{{post.title}}</h3>
|
||||
</header>
|
||||
{% if post.status == 'На выставке' %}
|
||||
<label class="offer-label">
|
||||
<input type="checkbox"
|
||||
onchange="saledPaintings('{{post._id}}')"
|
||||
class="offer-checkbox"
|
||||
{% if post.status == 'Продано' %}checked{%endif%}>
|
||||
<span>Продано</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
{% if post.status=='В каталоге' %}
|
||||
<div class="l_green"><span>{{post.status}}</span></div>
|
||||
<div class="l_green" id="SC"><span>{{post.status}}</span></div>
|
||||
{% else %}
|
||||
<div class="l_purple"><span>{{post.status}}</span></div>
|
||||
{% endif %}
|
||||
@ -92,6 +101,55 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<details style="margin-bottom: 1.25rem;">
|
||||
<summary>Показать контакты</summary>
|
||||
<div class="menua">
|
||||
<a class="icona" href="/artists/{{post.user._id}}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M234-276q51-39 114-61.5T480-360q69 0 132 22.5T726-276q35-41 54.5-93T800-480q0-133-93.5-226.5T480-800q-133 0-226.5 93.5T160-480q0 59 19.5 111t54.5 93Zm146.5-204.5Q340-521 340-580t40.5-99.5Q421-720 480-720t99.5 40.5Q620-639 620-580t-40.5 99.5Q539-440 480-440t-99.5-40.5ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm100-95.5q47-15.5 86-44.5-39-29-86-44.5T480-280q-53 0-100 15.5T294-220q39 29 86 44.5T480-160q53 0 100-15.5ZM523-537q17-17 17-43t-17-43q-17-17-43-17t-43 17q-17 17-17 43t17 43q17 17 43 17t43-17Zm-43-43Zm0 360Z" />
|
||||
</svg>
|
||||
<span>Профиль</span>
|
||||
</a>
|
||||
<a class="icona" href="tel:{{post.user.phone.split(',')[-1].strip()}}"><svg xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M162-120q-18 0-30-12t-12-30v-162q0-13 9-23.5t23-14.5l138-28q14-2 28.5 2.5T342-374l94 94q38-22 72-48.5t65-57.5q33-32 60.5-66.5T681-524l-97-98q-8-8-11-19t-1-27l26-140q2-13 13-22.5t25-9.5h162q18 0 30 12t12 30q0 125-54.5 247T631-329Q531-229 409-174.5T162-120Zm556-480q17-39 26-79t14-81h-88l-18 94 66 66ZM360-244l-66-66-94 20v88q41-3 81-14t79-28Zm358-356ZM360-244Z" />
|
||||
</svg>
|
||||
<span>{{ post.user.phone.split(',')[-1].strip() }}</span>
|
||||
</a>
|
||||
<a class="icona" href="mailto:{{post.user.email}}"> <svg xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 -960 960 960" width="24px" fill="#5F1798">
|
||||
<path
|
||||
d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480v58q0 59-40.5 100.5T740-280q-35 0-66-15t-52-43q-29 29-65.5 43.5T480-280q-83 0-141.5-58.5T280-480q0-83 58.5-141.5T480-680q83 0 141.5 58.5T680-480v58q0 26 17 44t43 18q26 0 43-18t17-44v-58q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93h200v80H480Zm85-315q35-35 35-85t-35-85q-35-35-85-35t-85 35q-35 35-35 85t35 85q35 35 85 35t85-35Z" />
|
||||
</svg>
|
||||
<span>{{post.user.email}}</span>
|
||||
</a>
|
||||
<div class="icona"><span>Написать</span></div>
|
||||
<div class="icona">
|
||||
|
||||
<a class="icona" href="{% if post.user.vk_id %}https://vk.ru/im?sel={{post.user.vk_id}}{%else%}#{%endif%}"
|
||||
{% if not post.user.vk_id %}onclick="showToast({message: 'У пользователя не привязан ВК'})"{%endif%}>
|
||||
<img src="/static/icons/vk.svg" height="40">
|
||||
</a>
|
||||
|
||||
<a class="icona" href="https://telegram.me/{{post.user.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/tg.svg" height="40">
|
||||
<!-- <span>Написать в ТГ</span> -->
|
||||
</a>
|
||||
<a class="icona" href="https://max.ru/{{post.user.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/max.svg" height="40" style="border-radius: 50% 50% 50% 30%;">
|
||||
<!-- <span>Написать в MAX</span> -->
|
||||
</a>
|
||||
<a class="icona" href="https://wa.me/{{post.user.phone.split(',')[-1].strip()}}">
|
||||
<img src="/static/icons/wapp.svg" height="40">
|
||||
<!-- <span>Написать в WA</span> -->
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</details>
|
||||
{% if post.status=='В каталоге' %}
|
||||
<button onclick="selectPost(this, '{{post._id}}')" class="c_ctfod b_289i1">
|
||||
<span class="c_d2l0b b_al8e7">Убрать с выставки</span>
|
||||
@ -115,6 +173,38 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script>
|
||||
function saledPaintings(postId) {
|
||||
fetch(`/api/post/${postId}/sale`, {
|
||||
method: 'PUT'
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
// Find and delete the parent figure
|
||||
const postElement = document.querySelector(`figure[data-postId='${postId}']`);
|
||||
showToast({
|
||||
message: 'Сохранено!',
|
||||
status: 'info',
|
||||
duration: 1200
|
||||
});
|
||||
} else {
|
||||
showToast({
|
||||
message: 'Ошибка соединения с сервером!',
|
||||
status: 'error',
|
||||
duration: 2500
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// Handle network errors
|
||||
showToast({
|
||||
message: 'Ошибка соединения с сервером!',
|
||||
status: 'error',
|
||||
duration: 2500
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{#
|
||||
<script>
|
||||
async function btnAccept(btn, itemId){
|
||||
@ -161,6 +251,10 @@
|
||||
async function sendInvitations(exhibition_id) {
|
||||
const data = new FormData();
|
||||
data.set('exhibition_id', exhibition_id);
|
||||
document.querySelectorAll('#SC').forEach(el=>{
|
||||
el.className = 'l_purple';
|
||||
el.querySelector('span').textContent = 'Ожидает принятия условий';
|
||||
})
|
||||
try {
|
||||
const res = await fetch('/api/exhibition/send/invitations', {
|
||||
method: 'POST',
|
||||
@ -211,8 +305,21 @@
|
||||
{{ profile_nav.style() }}
|
||||
{{ competition.style() }}
|
||||
<style>
|
||||
.menua{width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
margin: 10px 20px;}
|
||||
.icona{display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;}
|
||||
.icona>span{color: #5F1798;}
|
||||
{{label.green()}}
|
||||
{{label.purple()}}
|
||||
.b_0jst1 { color: #0ED646; font-family: Inter; font-size: 12px; font-style: normal; font-weight: 700; line-height: normal; }
|
||||
.b_vabj9 { align-items: center; background: rgba(14, 214, 70, 0.20); border-radius: 30px; display: flex; justify-content: center; padding: 1.5px 10px; }
|
||||
.b_0jst1_2 { color: #5F1798; font-family: Inter; font-size: 12px; font-style: normal; font-weight: 700; line-height: normal; }
|
||||
.b_vabj9_2 { align-items: center; background: #5e17982f; border-radius: 30px; display: flex; justify-content: center; padding: 1.5px 10px; }
|
||||
#preview_poster{min-height: 456px;}
|
||||
body{background: #F6EBFF;}
|
||||
.c_3ihvi{ position: sticky; top: 0; z-index: 2; background: white; }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user