Compare commits
4 Commits
v1.0.0
...
5751ab832f
| Author | SHA1 | Date | |
|---|---|---|---|
| 5751ab832f | |||
| 03d7bd8de6 | |||
| 3f04637550 | |||
| 79f7e18c8c |
BIN
android/store-assets/feature-graphic.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
android/store-assets/icon-512.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
android/store-assets/phone-1-tasks.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
android/store-assets/phone-2-calendar.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
android/store-assets/phone-3-chat.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
android/store-assets/phone-4-goals.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
android/store-assets/screenshot-1.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
android/store-assets/tablet10-1-tasks.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
android/store-assets/tablet10-2-calendar.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
android/store-assets/tablet7-1-tasks.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
android/store-assets/tablet7-2-calendar.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
114
api/package-lock.json
generated
@@ -16,6 +16,7 @@
|
||||
"@fastify/rate-limit": "^10.3.0",
|
||||
"@fastify/swagger": "^9.7.0",
|
||||
"@fastify/swagger-ui": "^5.2.5",
|
||||
"@fastify/websocket": "^11.2.0",
|
||||
"bcrypt": "^6.0.0",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"dotenv": "^17.3.1",
|
||||
@@ -367,6 +368,27 @@
|
||||
"yaml": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/websocket": {
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/websocket/-/websocket-11.2.0.tgz",
|
||||
"integrity": "sha512-3HrDPbAG1CzUCqnslgJxppvzaAZffieOVbLp1DAy1huCSynUWPifSvfdEDUR8HlJLp3sp1A36uOM2tJogADS8w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"duplexify": "^4.1.3",
|
||||
"fastify-plugin": "^5.0.0",
|
||||
"ws": "^8.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ioredis/commands": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.1.tgz",
|
||||
@@ -767,6 +789,18 @@
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/duplexify": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
|
||||
"integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.4.1",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1",
|
||||
"stream-shift": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/ecdsa-sig-formatter": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||
@@ -776,6 +810,15 @@
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
@@ -1462,6 +1505,15 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/openapi-types": {
|
||||
"version": "12.1.3",
|
||||
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz",
|
||||
@@ -1767,6 +1819,20 @@
|
||||
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
@@ -2016,6 +2082,21 @@
|
||||
"reusify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stream-shift": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
|
||||
"integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
@@ -2101,6 +2182,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
@@ -2142,6 +2229,33 @@
|
||||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
|
||||
"integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"@fastify/rate-limit": "^10.3.0",
|
||||
"@fastify/swagger": "^9.7.0",
|
||||
"@fastify/swagger-ui": "^5.2.5",
|
||||
"@fastify/websocket": "^11.2.0",
|
||||
"bcrypt": "^6.0.0",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"dotenv": "^17.3.1",
|
||||
|
||||
@@ -6,6 +6,7 @@ const jwt = require("@fastify/jwt");
|
||||
const rateLimit = require("@fastify/rate-limit");
|
||||
const { Pool } = require("pg");
|
||||
const Redis = require("ioredis");
|
||||
const websocket = require("@fastify/websocket");
|
||||
const swagger = require("@fastify/swagger");
|
||||
const swaggerUi = require("@fastify/swagger-ui");
|
||||
|
||||
@@ -85,6 +86,16 @@ const start = async () => {
|
||||
uiConfig: { docExpansion: "list", deepLinking: true }
|
||||
});
|
||||
|
||||
// WebSocket support
|
||||
await app.register(websocket);
|
||||
|
||||
app.get("/ws", { websocket: true }, (socket, req) => {
|
||||
socket.on("message", (msg) => {
|
||||
socket.send(JSON.stringify({ type: "ack", message: msg.toString() }));
|
||||
});
|
||||
socket.send(JSON.stringify({ type: "connected", timestamp: new Date().toISOString() }));
|
||||
});
|
||||
|
||||
// Register routes
|
||||
await app.register(require("./routes/tasks"), { prefix: "/api/v1" });
|
||||
await app.register(require("./routes/groups"), { prefix: "/api/v1" });
|
||||
|
||||
@@ -101,8 +101,37 @@ async function authRoutes(app) {
|
||||
return { status: 'not_implemented', message: 'WebAuthn biometric auth coming soon.' };
|
||||
});
|
||||
|
||||
// Delete account
|
||||
app.delete('/auth/account', { preHandler: [async (req) => { await req.jwtVerify(); }] }, async (req, reply) => {
|
||||
const uid = req.user.id;
|
||||
await app.db.query('DELETE FROM task_assignments WHERE user_id=$1 OR assigned_by=$1', [uid]);
|
||||
await app.db.query('DELETE FROM tasks WHERE user_id=$1', [uid]);
|
||||
await app.db.query('DELETE FROM task_groups WHERE user_id=$1', [uid]);
|
||||
await app.db.query('DELETE FROM goals WHERE user_id=$1', [uid]);
|
||||
await app.db.query('DELETE FROM sessions WHERE user_id=$1', [uid]);
|
||||
await app.db.query('DELETE FROM users WHERE id=$1', [uid]);
|
||||
return reply.send({ data: { deleted: true } });
|
||||
});
|
||||
|
||||
// OAuth initiate routes moved to ./oauth.js
|
||||
|
||||
|
||||
// Export all user data (GDPR)
|
||||
app.get("/auth/export-data", { preHandler: [async (req) => { await req.jwtVerify(); }] }, async (req) => {
|
||||
const userId = req.user.id;
|
||||
const { rows: user } = await app.db.query("SELECT id,email,name,phone,language,created_at FROM users WHERE id=$1", [userId]);
|
||||
const { rows: tasks } = await app.db.query("SELECT * FROM tasks WHERE user_id=$1", [userId]);
|
||||
const { rows: goals } = await app.db.query("SELECT * FROM goals WHERE user_id=$1", [userId]);
|
||||
const { rows: groups } = await app.db.query("SELECT * FROM task_groups WHERE user_id=$1 OR user_id IS NULL", [userId]);
|
||||
const { rows: comments } = await app.db.query("SELECT * FROM task_comments WHERE user_id=$1", [userId]);
|
||||
return {
|
||||
exported_at: new Date().toISOString(),
|
||||
user: user[0] || {},
|
||||
tasks, goals, groups, comments,
|
||||
total: { tasks: tasks.length, goals: goals.length, comments: comments.length }
|
||||
};
|
||||
});
|
||||
|
||||
// Search users by name or email (for collaboration)
|
||||
app.get('/auth/users/search', async (req) => {
|
||||
const q = (req.query.q || '').trim();
|
||||
@@ -117,3 +146,4 @@ async function authRoutes(app) {
|
||||
}
|
||||
|
||||
module.exports = authRoutes;
|
||||
|
||||
|
||||
34
apps/tasks/app/privacy/page.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
export default function PrivacyPage() {
|
||||
return (
|
||||
<div style={{ maxWidth: 800, margin: "0 auto", padding: "40px 24px", fontFamily: "system-ui", lineHeight: 1.8 }}>
|
||||
<h1 style={{ fontSize: 28, fontWeight: "bold", marginBottom: 24 }}>Privacy Policy - Task Team</h1>
|
||||
<p style={{ color: "#666", marginBottom: 16 }}>Last updated: March 30, 2026</p>
|
||||
|
||||
<h2 style={{ fontSize: 20, fontWeight: 600, marginTop: 32, marginBottom: 12 }}>Data We Collect</h2>
|
||||
<p>Account info (name, email), task data, GPS location (optional), audio for voice-to-text (optional).</p>
|
||||
|
||||
<h2 style={{ fontSize: 20, fontWeight: 600, marginTop: 32, marginBottom: 12 }}>How We Use It</h2>
|
||||
<p>Task management, sync across devices, reminders, GPS auto-verify task completion.</p>
|
||||
|
||||
<h2 style={{ fontSize: 20, fontWeight: 600, marginTop: 32, marginBottom: 12 }}>Data Storage</h2>
|
||||
<p>Your data is stored on secure servers in the European Union (Hetzner Cloud, Germany). We use PostgreSQL databases with encrypted connections and regular backups.</p>
|
||||
|
||||
<h2 style={{ fontSize: 20, fontWeight: 600, marginTop: 32, marginBottom: 12 }}>Data Sharing</h2>
|
||||
<p>We do NOT sell data. Services used: Hetzner Cloud Germany, Anthropic Claude AI.</p>
|
||||
|
||||
<h2 style={{ fontSize: 20, fontWeight: 600, marginTop: 32, marginBottom: 12 }}>Your Rights (GDPR)</h2>
|
||||
<p>You have the right to access, correct, and delete your data. Contact: privacy@it-enterprise.cz</p>
|
||||
|
||||
<h2 style={{ fontSize: 20, fontWeight: 600, marginTop: 32, marginBottom: 12 }}>Permissions</h2>
|
||||
<p>Location and microphone are optional, revocable anytime in your device settings.</p>
|
||||
|
||||
<h2 style={{ fontSize: 20, fontWeight: 600, marginTop: 32, marginBottom: 12 }}>Children</h2>
|
||||
<p>Not directed to children under 13.</p>
|
||||
|
||||
<h2 style={{ fontSize: 20, fontWeight: 600, marginTop: 32, marginBottom: 12 }}>Contact</h2>
|
||||
<p>IT Enterprise s.r.o. | privacy@it-enterprise.cz | tasks.hasdo.info</p>
|
||||
|
||||
<p style={{ marginTop: 32, color: "#999", fontSize: 14 }}>© 2026 IT Enterprise s.r.o. All rights reserved.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -217,6 +217,21 @@ export default function SettingsPage() {
|
||||
<div className="text-center text-xs text-muted py-4">
|
||||
<p>{t("common.appName")} {t("common.appVersion")}</p>
|
||||
</div>
|
||||
|
||||
<section className="bg-red-50 dark:bg-red-900/20 rounded-xl p-4 border border-red-200 dark:border-red-800 mt-6">
|
||||
<h2 className="font-semibold text-red-600 dark:text-red-400 mb-2">Smazat ucet</h2>
|
||||
<p className="text-sm text-red-500 mb-3">Trvale smazat ucet a vsechna data. Tuto akci nelze vratit.</p>
|
||||
<button onClick={() => {
|
||||
if (confirm("Opravdu chcete trvale smazat svuj ucet a vsechna data?")) {
|
||||
const token = localStorage.getItem("taskteam_token");
|
||||
fetch("/api/v1/auth/delete-account", { method: "DELETE", headers: { Authorization: "Bearer " + token } })
|
||||
.then(r => r.json())
|
||||
.then(() => { localStorage.clear(); window.location.href = "/login"; });
|
||||
}
|
||||
}} className="px-4 py-2 bg-red-600 text-white rounded-lg text-sm font-medium">
|
||||
Smazat ucet
|
||||
</button>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -130,17 +130,16 @@ export default function TasksPage() {
|
||||
|
||||
return (
|
||||
<div className="space-y-2 pb-24 sm:pb-8 px-4 sm:px-0">
|
||||
{/* Group dropdown + Status pills row */}
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
{/* Group dropdown + Status pills — single compact row */}
|
||||
<div className="flex items-center gap-3 flex-nowrap">
|
||||
<div className="flex-shrink-0">
|
||||
<GroupSelector
|
||||
groups={groups}
|
||||
selected={selectedGroup}
|
||||
onSelect={setSelectedGroup}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Status filter pills - compact */}
|
||||
<div className="flex gap-1 overflow-x-auto scrollbar-hide px-4">
|
||||
<div className="flex gap-1 overflow-x-auto scrollbar-hide flex-1 min-w-0">
|
||||
{statusOptions.map((opt) => (
|
||||
<button
|
||||
key={opt.value}
|
||||
@@ -155,6 +154,7 @@ export default function TasksPage() {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Swipeable task list area */}
|
||||
<div {...swipeHandlers} className="relative min-h-[200px]">
|
||||
|
||||
11
backup.sh
@@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
cd /opt/task-team || exit 1
|
||||
BACKUP_DIR="/opt/task-team/backups"
|
||||
DATE=$(date +%Y%m%d_%H%M)
|
||||
PGDUMP="/usr/lib/postgresql/18/bin/pg_dump"
|
||||
@@ -10,13 +11,13 @@ PGPASSWORD="TaskTeam2026!" $PGDUMP -h 10.10.10.10 -U taskteam -d taskteam -F c -
|
||||
if [ $? -eq 0 ]; then
|
||||
# Compress
|
||||
gzip -f "$BACKUP_DIR/taskteam_$DATE.dump" 2>/dev/null
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') OK: taskteam_$DATE.dump.gz"
|
||||
echo "$(date "+%Y-%m-%d %H:%M:%S") OK: taskteam_$DATE.dump.gz"
|
||||
else
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') FAIL: pg_dump exited with error"
|
||||
echo "$(date "+%Y-%m-%d %H:%M:%S") FAIL: pg_dump exited with error"
|
||||
fi
|
||||
|
||||
# Keep last 7 daily backups
|
||||
find $BACKUP_DIR -name "*.dump.gz" -mtime +7 -delete
|
||||
find $BACKUP_DIR -name "*.dump" -mtime +7 -delete
|
||||
# Keep last 14 daily backups
|
||||
find $BACKUP_DIR -name "*.dump.gz" -mtime +14 -delete
|
||||
find $BACKUP_DIR -name "*.dump" -mtime +14 -delete
|
||||
|
||||
ls -lh $BACKUP_DIR/ | tail -5
|
||||
|
||||
BIN
backups/releases/android/task-team-v1.0.0.apk
Normal file
BIN
backups/taskteam_20260329_2147.dump.gz
Normal file
@@ -13,7 +13,10 @@
|
||||
},
|
||||
"ios": {
|
||||
"supportsTablet": true,
|
||||
"bundleIdentifier": "info.hasdo.taskteam"
|
||||
"bundleIdentifier": "info.hasdo.taskteam",
|
||||
"infoPlist": {
|
||||
"ITSAppUsesNonExemptEncryption": false
|
||||
}
|
||||
},
|
||||
"android": {
|
||||
"adaptiveIcon": {
|
||||
|
||||
39
mobile/fdroid/metadata/info.hasdo.taskteam.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
Categories:
|
||||
- Office
|
||||
- Time
|
||||
License: AGPL-3.0-only
|
||||
AuthorName: IT Enterprise Solution s.r.o.
|
||||
AuthorEmail: apps@it-enterprise.cz
|
||||
AuthorWebSite: https://it-enterprise.cz
|
||||
SourceCode: https://git.hasdo.info/admin/task-team
|
||||
IssueTracker: https://git.hasdo.info/admin/task-team/issues
|
||||
|
||||
AutoName: Task Team
|
||||
Description: |
|
||||
Personal life management system with AI assistant.
|
||||
Manage tasks, goals, calendar, and team collaboration.
|
||||
|
||||
Features:
|
||||
* Task management with groups and priorities
|
||||
* Calendar with color-coded time zones
|
||||
* AI chat assistant (Claude)
|
||||
* Goal planning with AI study planner
|
||||
* Team collaboration (assign, transfer, subtasks)
|
||||
* Multi-language (CZ, HE, RU, UA)
|
||||
* Odoo, Moodle, Pohoda connectors
|
||||
|
||||
RepoType: git
|
||||
Repo: https://git.hasdo.info/admin/task-team.git
|
||||
|
||||
Builds:
|
||||
- versionName: 1.0.0
|
||||
versionCode: 1
|
||||
commit: v1.0.0
|
||||
subdir: mobile
|
||||
gradle:
|
||||
- yes
|
||||
|
||||
AutoUpdateMode: Version
|
||||
UpdateCheckMode: Tags
|
||||
CurrentVersion: 1.0.0
|
||||
CurrentVersionCode: 1
|
||||
36
mobile/store-listing/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Task Team — Store Listing
|
||||
|
||||
## Short Description (80 chars)
|
||||
Osobní systém řízení života s AI asistentem. Úkoly, cíle, kalendář, spolupráce.
|
||||
|
||||
## Full Description
|
||||
Task Team je komplexní systém řízení osobního života propojený s AI agentem.
|
||||
|
||||
**Hlavní funkce:**
|
||||
- Správa úkolů s barevnými skupinami a prioritami
|
||||
- Kalendář s časovými zónami pro každou skupinu
|
||||
- AI chat asistent pro plánování a doporučení
|
||||
- Cílový planner s AI generovanými studijními plány
|
||||
- Týmová spolupráce: přiřazení, předání, podúkoly
|
||||
- 4 jazyky: čeština, hebrejština, ruština, ukrajinština
|
||||
- Connectors: Odoo, Moodle, Pohoda
|
||||
- PWA + Android + iOS
|
||||
|
||||
**Technologie:** React Native, Expo, Fastify, PostgreSQL, Redis, Claude AI
|
||||
|
||||
## Links
|
||||
- Web: https://tasks.hasdo.info
|
||||
- API Docs: https://api.hasdo.info/docs
|
||||
- Source: https://git.hasdo.info/admin/task-team
|
||||
- Android: https://expo.dev/accounts/it-enterprise/projects/task-team
|
||||
- Company: IT Enterprise Solution s.r.o.
|
||||
- Contact: apps@it-enterprise.cz
|
||||
|
||||
## APKPure Submission
|
||||
1. Go to https://apkpure.com/submit-apk
|
||||
2. Upload APK from Expo build
|
||||
3. Fill in listing details from above
|
||||
|
||||
## Aurora Store
|
||||
Aurora Store indexes F-Droid and other repos automatically.
|
||||
Once on F-Droid, it appears in Aurora.
|
||||
@@ -5,7 +5,7 @@ set -euo pipefail
|
||||
|
||||
VERSION=${1:-$(date +%Y%m%d_%H%M)}
|
||||
PLATFORM=${2:-android}
|
||||
FILE=$3
|
||||
FILE=${3:-}
|
||||
|
||||
if [ -z "${FILE:-}" ]; then
|
||||
echo "Usage: upload-release.sh <version> <platform> <file>"
|
||||
|
||||
15
scripts/weekly-report.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
# AI Weekly Report — runs every Monday 8:00 AM
|
||||
export ANTHROPIC_API_KEY="$(grep ANTHROPIC_API_KEY /opt/task-team/api/.env | cut -d= -f2)"
|
||||
|
||||
# Get task stats
|
||||
STATS=$(curl -s http://localhost:3000/api/v1/system/health | python3 -c "import sys,json; d=json.load(sys.stdin); print(json.dumps(d.get(tasks,{})))" 2>/dev/null || echo "{}")
|
||||
|
||||
# Generate AI report
|
||||
REPORT=$(curl -s -X POST http://localhost:3000/api/v1/chat \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"message\":\"Vytvor tydeni report. Stats: $STATS. Shrn co se podarilo, co je treba zlepsit, doporuceni na pristi tyden. Odpovez v cestine, strucne.\"}" | python3 -c "import sys,json; print(json.load(sys.stdin).get(data,{}).get(reply,No report))" 2>/dev/null || echo "Report generation failed")
|
||||
|
||||
# Log
|
||||
echo "$(date): $REPORT" >> /var/log/taskteam-weekly.log
|
||||
echo "$REPORT"
|
||||